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    assert!(
4628        dialects.len() == 1
4629            || (dialects.len() == 2
4630                && dialects[0] != LanguageDialect::CppC
4631                && dialects[1] == LanguageDialect::CppC),
4632        "one primary dialect, optionally followed by its C alternative"
4633    );
4634    let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4635    let properties = RefCell::new(HashMap::default());
4636    let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4637    let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4638    let mut using_namespaces = Vec::new();
4639    let mut namespace_aliases = Vec::new();
4640    let mut readings: Vec<_> = dialects
4641        .iter()
4642        .map(|_| ParsedFile::new(String::new()))
4643        .collect();
4644    let mut visitors: Vec<_> = readings
4645        .iter_mut()
4646        .zip(dialects)
4647        .map(|(parsed, dialect)| CppVisitor {
4648            file,
4649            source,
4650            parsed,
4651            source_collector: &collector,
4652            source_properties: &properties,
4653            source_dependencies: &dependencies,
4654            primary_ancestry: ancestry,
4655            primary_root_id: root.id(),
4656            source_links: HashMap::default(),
4657            source_declarations: HashSet::default(),
4658            preprocessor_context_cache: Default::default(),
4659            c_tag_semantics: *dialect == LanguageDialect::CppC,
4660            c_tag_scope_witness: false,
4661            recovered_class_sibling_scopes: HashMap::default(),
4662            consumed_fragment_regions: Vec::new(),
4663            orphaned_namespaces: &orphaned_namespaces,
4664            partitioned_regions: Vec::new(),
4665            namespace_forward_scans: HashMap::default(),
4666            recovery_captures: Vec::new(),
4667            active_source_context: None,
4668            object_macro_fields: object_macro_fields.clone(),
4669            ambiguous_object_macro_fields: HashSet::default(),
4670        })
4671        .collect();
4672    let mut declaration_stacks: Vec<_> = dialects
4673        .iter()
4674        .map(|_| {
4675            vec![CppWork::Container(CppContainer {
4676                node: root,
4677                scope: ScopeInfo {
4678                    package_name: String::new(),
4679                    module: None,
4680                    class_unit: None,
4681                    template_signature: None,
4682                    template_metadata: None,
4683                    declarations_are_fields: false,
4684                    recovered_specialization_member_scope: false,
4685                    visible_using_namespaces: Vec::new(),
4686                    local_block: None,
4687                    visible_using_declarations: Arc::default(),
4688                },
4689            })]
4690        })
4691        .collect();
4692    let kinds = CompiledKinds::compile(
4693        &tree_sitter_cpp::LANGUAGE.into(),
4694        CPP_STRUCTURAL_SPEC.kind_table(),
4695    );
4696    // Forward macro names affect calls before their definitions and require the
4697    // existing bounded preprocessor-only context prepass.
4698    let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4699    let mut structural = StructuralFactCollector::new(
4700        &CPP_STRUCTURAL_SPEC,
4701        source,
4702        &context,
4703        brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4704        usize::MAX,
4705        None,
4706    );
4707    let mut imports = Vec::new();
4708    let mut includes = Vec::new();
4709    let mut expressions = Vec::new();
4710    let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4711    let mut statement_facts = StatementFactRows::default();
4712    let mut identifiers = HashSet::default();
4713    let mut entered = HashSet::default();
4714    let mut primary_ordinals = HashMap::default();
4715    let mut stack = vec![(root, None)];
4716    while let Some((node, parent)) = stack.pop() {
4717        assert!(entered.insert(node.id()), "one primary event per AST node");
4718        primary_ordinals.insert(node.id(), primary_ordinals.len());
4719        dependencies.borrow_mut().primary.enter(node, source);
4720        let mut child_parent = parent;
4721        if node.is_named() {
4722            crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4723            if let Some(namespace) = cpp_using_namespace_target(node, source) {
4724                using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4725            }
4726            if let Some(alias) = crate::source_facts::capture_namespace_alias(
4727                node,
4728                source,
4729                ancestry,
4730                &orphaned_namespaces,
4731                &mut collector.borrow_mut(),
4732            ) {
4733                namespace_aliases.push(alias);
4734            }
4735            if let Some(raw_kind) = kinds.kind_of(&node)
4736                && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4737            {
4738                let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4739                    node,
4740                    raw_kind,
4741                    parent.map(|id| structural.normalized_kind(id)),
4742                    source,
4743                    &context,
4744                );
4745                let mut source_facts = collector.borrow_mut();
4746                let id = structural
4747                    .enter(node, kind, parent, &mut source_facts)
4748                    .expect("unbounded C++ structural collector");
4749                let mut sink = structural.role_sink(&mut source_facts);
4750                CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4751                structural
4752                    .accept_roles(id, sink.into_parts())
4753                    .expect("unbounded C++ structural roles");
4754                child_parent = Some(id);
4755            }
4756            if let Some(fact) = crate::source_facts::capture_expression_fact(
4757                node,
4758                source,
4759                collector.borrow_mut().intern_node(node),
4760                &named_record_sizes,
4761            ) {
4762                expressions.push(fact);
4763            }
4764            if node.kind() == "preproc_include" {
4765                let raw = normalize_cpp_whitespace(node_text(node, source));
4766                if !raw.is_empty() {
4767                    let occurrence = collector.borrow_mut().intern_node(node);
4768                    let target = node.child_by_field_name("path").and_then(|path| {
4769                        let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4770                        let target = collector.borrow_mut().intern_node(path);
4771                        includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4772                            declaration: occurrence,
4773                            target,
4774                            path: value.to_owned(),
4775                            quoted,
4776                        });
4777                        Some(target)
4778                    });
4779                    imports.push(SourceImportFact::from_import(
4780                        cpp_include_info(raw),
4781                        occurrence,
4782                        target,
4783                        None,
4784                        Vec::new(),
4785                    ));
4786                }
4787            }
4788            if matches!(
4789                node.kind(),
4790                "type_identifier" | "identifier" | "qualified_identifier"
4791            ) {
4792                let text = node_text(node, source).trim();
4793                if !text.is_empty() {
4794                    identifiers.insert(text.to_owned());
4795                }
4796            }
4797        }
4798        for index in 0..visitors.len() {
4799            // Delay the alternative policy until the primary walk encounters a
4800            // tag whose scope can differ. Its saved declaration stack catches up
4801            // through already-entered events, including recovery, without a
4802            // second AST traversal. A false witness performs no C declaration work.
4803            if index != 0 && !visitors[0].c_tag_scope_witness {
4804                continue;
4805            }
4806            let visitor = &mut visitors[index];
4807            advance_primary_declarations(
4808                visitor,
4809                &mut declaration_stacks[index],
4810                &entered,
4811                ancestry,
4812            );
4813            visitor.drain_partitioned_regions();
4814        }
4815        let mut cursor = node.walk();
4816        let children: Vec<_> = node
4817            .children(&mut cursor)
4818            .map(|child| (child, child_parent))
4819            .collect();
4820        stack.extend(children.into_iter().rev());
4821    }
4822    let reading_count = if visitors[0].c_tag_scope_witness {
4823        visitors.len()
4824    } else {
4825        1
4826    };
4827    visitors.truncate(reading_count);
4828    declaration_stacks.truncate(reading_count);
4829    assert!(
4830        declaration_stacks.iter().all(Vec::is_empty),
4831        "all primary declaration events were consumed"
4832    );
4833    for visitor in &mut visitors {
4834        visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4835            .into_iter()
4836            .flat_map(|(unit, declarations)| {
4837                declarations
4838                    .into_iter()
4839                    .map(move |declaration| (declaration, unit.clone()))
4840            })
4841            .collect();
4842    }
4843    let dialect_declarations: Vec<_> = visitors
4844        .iter()
4845        .map(|visitor| visitor.source_declarations.clone())
4846        .collect();
4847    drop(visitors);
4848    readings.truncate(reading_count);
4849    dependencies
4850        .borrow_mut()
4851        .finish_primary(&mut properties.borrow_mut());
4852    // Preserve the existing malformed quoted-directive recovery, but publish its
4853    // explicit source occurrence into the same import family as parsed directives.
4854    recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4855    let primary_occurrence_ordinals: HashMap<_, _> = collector
4856        .borrow()
4857        .primary_node_occurrences()
4858        .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4859        .collect();
4860    let occurrences = collector.into_inner().finish();
4861    let generic_imports: Vec<_> = (0..imports.len())
4862        .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4863        .collect();
4864    let source_facts = ParsedSourceFacts {
4865        cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4866            includes,
4867            declarations: {
4868                let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4869                facts.sort_by_key(|fact| fact.declaration.get());
4870                facts
4871            },
4872            using_namespaces,
4873            namespace_aliases,
4874            expressions,
4875            statement_facts,
4876        }),
4877        go: None,
4878        java: None,
4879        js_ts: None,
4880        php: None,
4881        scala: None,
4882        ruby: None,
4883        python: None,
4884        source_bytes: source.len(),
4885        occurrences,
4886        structural: structural
4887            .finish(root)
4888            .expect("C++ structural collection finishes"),
4889        native_site_occurrences: Vec::new(),
4890        native_declaration_sources: Vec::new(),
4891        declaration_visibilities: None,
4892        rust_declaration_properties: Vec::new(),
4893        rust_modules: None,
4894        rust_types: Vec::new(),
4895        rust_items: Default::default(),
4896        imports,
4897        generic_imports,
4898        rust_import_contexts: Vec::new(),
4899    };
4900    for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4901        parsed.finalize_deferred_replacements();
4902        let links = std::mem::take(&mut parsed.source_declaration_units);
4903        parsed.source_declaration_units = links
4904            .into_iter()
4905            .filter(|(_, unit)| parsed.contains_declaration(unit))
4906            .collect();
4907        parsed.imports = source_facts
4908            .imports
4909            .iter()
4910            .map(|import| import.import_info(&source_facts.occurrences))
4911            .collect();
4912        parsed.type_identifiers = identifiers.clone();
4913        parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4914            &source_facts,
4915            &declarations,
4916            &primary_occurrence_ordinals,
4917            &mut parsed.source_declaration_units,
4918            &mut parsed.source_declaration_metadata,
4919        ));
4920    }
4921    if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4922        // The recovery runs to the end of the file, which is how a reader of
4923        // the record tells this recovery from a local one (#3501).
4924        let recovery = Range {
4925            start_byte: outermost.start_byte(),
4926            end_byte: source.len(),
4927            start_line: outermost.start_position().row + 1,
4928            end_line: root.end_position().row + 1,
4929        };
4930        for parsed in &mut readings {
4931            record_declarations_left_open(parsed, recovery);
4932        }
4933    }
4934    readings
4935}
4936
4937fn advance_primary_declarations<'tree>(
4938    visitor: &mut CppVisitor<'_, '_>,
4939    stack: &mut Vec<CppWork<'tree>>,
4940    entered: &HashSet<usize>,
4941    ancestry: &ParentIndex<'tree>,
4942) {
4943    while let Some(work) = stack.pop() {
4944        match work {
4945            CppWork::Container(container) => {
4946                push_cpp_container_work(container.node, container.scope, stack)
4947            }
4948            CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4949            CppWork::Node(work) => {
4950                if !entered.contains(&work.node.id()) {
4951                    stack.push(CppWork::Node(work));
4952                    return;
4953                }
4954                if !visitor.node_is_inside_consumed_fragment(work.node) {
4955                    visitor.visit_node(work.node, &work.scope, stack, ancestry);
4956                }
4957            }
4958        }
4959    }
4960}
4961
4962#[derive(Clone)]
4963struct CppActiveSourceContext {
4964    guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4965    namespace: Option<String>,
4966    child_namespace: Option<String>,
4967    outer_lexical_scope: Vec<String>,
4968    child_lexical_scope: Vec<String>,
4969    family: Option<SourceOccurrenceId>,
4970    exhaustive_family: Option<SourceOccurrenceId>,
4971    callable_guard_completion_byte: Option<usize>,
4972}
4973
4974/// One owned container reparse deferred until the primary declaration walk has
4975/// reached its source position. The tree and context are owned so the deferred
4976/// walk never retains a `Node` borrowed from a temporary recovery tree.
4977pub struct CppPartitionedRegion {
4978    tree: Tree,
4979    range: std::ops::Range<usize>,
4980    scope: ScopeInfo,
4981    recovery: Range,
4982    source_context: Option<CppActiveSourceContext>,
4983}
4984
4985pub struct CppVisitor<'a, 'source> {
4986    pub file: &'a ProjectFile,
4987    pub source: &'source str,
4988    pub parsed: &'a mut ParsedFile,
4989    pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4990    source_properties: &'a RefCell<
4991        HashMap<
4992            SourceDeclarationId,
4993            brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4994        >,
4995    >,
4996    source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4997    pub primary_ancestry: &'a ParentIndex<'a>,
4998    pub primary_root_id: usize,
4999    source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
5000    source_declarations: HashSet<SourceDeclarationId>,
5001    preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
5002    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
5003    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
5004    ///
5005    /// C has no nested tag scope: a struct/union/enum tag declared inside
5006    /// another aggregate's member list has the scope of the outer declaration
5007    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
5008    /// therefore declares a file-scope `inner` that a later file-scope
5009    /// `struct inner *p;` legitimately references, where C++ would make the
5010    /// same shape a nested class `outer::inner`. Headers carry no compilation
5011    /// language of their own and keep the conservative C++ interpretation.
5012    pub c_tag_semantics: bool,
5013    /// Sticky evidence that this C++ walk reached a declaration whose scope
5014    /// would change under C tag semantics, including visitor recovery work.
5015    pub(crate) c_tag_scope_witness: bool,
5016    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
5017    /// Byte regions whose contents were re-owned by a fragmented export-class
5018    /// recovery (#938): the scattered members between the fragmented
5019    /// declaration and its displaced closing brace are indexed as members of
5020    /// the recovered class by the region reparse, so the ordinary sibling walk
5021    /// must not ALSO index them as top-level declarations (that double-indexing
5022    /// made a scattered nested class ambiguous between `Inner` and
5023    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
5024    /// linear scan at visit time is fine.
5025    pub consumed_fragment_regions: Vec<(usize, usize)>,
5026    /// Lexical namespace corrections and brace boundaries for declarations
5027    /// displaced by parse recovery (issues #1537 and #3087). The parsed
5028    /// ancestors can stop short or extend past their true closes; see
5029    /// [`CppVisitor::recovered_namespace_scope`].
5030    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
5031    /// Owned reparses waiting for the outer work loop. Partitioning another
5032    /// swallowed class in a tail must not grow the Rust call stack.
5033    pub partitioned_regions: Vec<CppPartitionedRegion>,
5034    /// The namespace forward declarations already folded out of each tree this
5035    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
5036    /// Empty until the first question, which the overwhelming majority of files
5037    /// never ask.
5038    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
5039    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
5040    /// happen to the declaration set, innermost last. Empty outside a recovery
5041    /// reparse, which is almost always (#2787).
5042    pub recovery_captures: Vec<CppRecoveryCapture>,
5043    active_source_context: Option<CppActiveSourceContext>,
5044    /// Object-like field-list macros defined earlier in this source. Their
5045    /// replacements are parsed structurally and materialized under each
5046    /// invoking aggregate; no source-text expansion is used.
5047    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
5048    /// Names whose active replacement is not a unique structured field list.
5049    /// A later `#undef` resets the ambiguity; another `#define` does not.
5050    pub ambiguous_object_macro_fields: HashSet<String>,
5051}
5052
5053/// One source-order event from an object-like field-list macro environment.
5054///
5055/// The event stream lets an include-closure consumer preserve preprocessor
5056/// invalidation without guessing which parsed header happened to be visited
5057/// first. A conservative consumer may permanently block a name after an
5058/// `undef` when conditional include order is not provable.
5059#[derive(Clone, Debug, PartialEq, Eq)]
5060pub enum ObjectMacroFieldEvent {
5061    Define {
5062        name: String,
5063        replacement: ObjectMacroReplacement,
5064        conditional: bool,
5065    },
5066    Undef {
5067        name: String,
5068        conditional: bool,
5069    },
5070}
5071
5072/// Collect object-like field-list macro events in source order. Conditional
5073/// branches remain visible in the event stream: callers that cannot prove
5074/// branch selection can invalidate names conservatively instead of inventing
5075/// a replacement from one branch.
5076pub fn collect_cpp_object_macro_field_events<'tree>(
5077    root: Node<'tree>,
5078    source: &str,
5079) -> Vec<ObjectMacroFieldEvent> {
5080    let mut events = Vec::new();
5081    let mut stack = vec![root];
5082    while let Some(node) = stack.pop() {
5083        if node.kind() == "preproc_def"
5084            && let Some(name) = extract_macro_name(node, source)
5085        {
5086            let replacement = object_macro_replacement_of(node, source);
5087            events.push(ObjectMacroFieldEvent::Define {
5088                name,
5089                replacement,
5090                conditional: inside_preprocessor_conditional(node),
5091            });
5092        } else if is_cpp_undef_directive(node, source)
5093            && let Some(argument) = node.child_by_field_name("argument")
5094        {
5095            events.push(ObjectMacroFieldEvent::Undef {
5096                name: node_text(argument, source).trim().to_string(),
5097                conditional: inside_preprocessor_conditional(node),
5098            });
5099        }
5100        let mut cursor = node.walk();
5101        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5102        stack.extend(children.into_iter().rev());
5103    }
5104    events
5105}
5106
5107fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5108    let mut current = node.parent();
5109    while let Some(parent) = current {
5110        if is_cpp_preprocessor_conditional(parent) {
5111            return true;
5112        }
5113        current = parent.parent();
5114    }
5115    false
5116}
5117
5118fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5119    node.kind() == "preproc_call"
5120        && node
5121            .child_by_field_name("directive")
5122            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5123}
5124
5125impl<'a, 'source> CppVisitor<'a, 'source> {
5126    fn apply_active_source_context(
5127        &self,
5128        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5129    ) {
5130        let Some(context) = self.active_source_context.as_ref() else {
5131            return;
5132        };
5133        fact.guard_requirements = fact
5134            .guard_requirements
5135            .as_ref()
5136            .and_then(crate::source_context::cpp_guard_set_to_runtime)
5137            .zip(context.guards.as_ref())
5138            .and_then(|(local, outer)| {
5139                crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5140            })
5141            .as_ref()
5142            .map(crate::source_context::cpp_guard_set_from_runtime);
5143        if fact.callable_guards.is_some() {
5144            fact.callable_guards = fact
5145                .callable_guards
5146                .as_ref()
5147                .and_then(crate::source_context::cpp_guard_set_to_runtime)
5148                .zip(context.guards.as_ref())
5149                .and_then(|(local, outer)| {
5150                    crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5151                })
5152                .as_ref()
5153                .map(crate::source_context::cpp_guard_set_from_runtime);
5154        }
5155        fact.callable_guard_completion_byte = fact
5156            .callable_guard_completion_byte
5157            .zip(context.callable_guard_completion_byte)
5158            .map(|(local, outer)| local.max(outer));
5159        fact.conditional_family = fact.conditional_family.or(context.family);
5160        fact.exhaustive_conditional_family = fact
5161            .exhaustive_conditional_family
5162            .or(context.exhaustive_family);
5163        if let Some(alias) = fact.file_scope_alias.as_mut() {
5164            alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5165                (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5166                (Some(outer), None) => Some(outer.clone()),
5167                (None, local) => local,
5168            };
5169        }
5170        if !context.outer_lexical_scope.is_empty() {
5171            let mut lexical = context.outer_lexical_scope.clone();
5172            lexical.append(&mut fact.lexical_path);
5173            fact.lexical_path = lexical;
5174        }
5175    }
5176
5177    fn recovery_ancestry<'tree>(
5178        &self,
5179        root: Node<'tree>,
5180    ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5181        crate::source_dependencies::RecoveryAncestry::new(
5182            root,
5183            self.source,
5184            self.source_dependencies,
5185        )
5186    }
5187
5188    fn capture_source_dependencies(
5189        &self,
5190        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5191        node: Node<'_>,
5192        unit: &CodeUnit,
5193    ) {
5194        if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5195            self.source_dependencies.borrow_mut().register_primary(
5196                fact.declaration,
5197                node,
5198                unit.is_class(),
5199            );
5200        } else {
5201            self.source_dependencies
5202                .borrow()
5203                .apply_recovered(node, unit.is_class(), fact);
5204        }
5205    }
5206
5207    fn link_source_declaration(
5208        &mut self,
5209        ancestry: &ParentIndex<'_>,
5210        unit: &CodeUnit,
5211        node: Node<'_>,
5212    ) {
5213        let (declaration, family) = {
5214            let mut collector = self.source_collector.borrow_mut();
5215            let occurrence = if node.id() == self.primary_root_id
5216                || self.primary_ancestry.contains(node)
5217            {
5218                collector.intern_node(node)
5219            } else {
5220                // A region reparse owns distinct provenance even when its byte range
5221                // equals a primary node or a previously dropped recovery tree.
5222                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5223            };
5224            let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5225                if self.primary_ancestry.contains(name) {
5226                    collector.intern_node(name)
5227                } else {
5228                    collector
5229                        .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5230                }
5231            });
5232            let declaration = collector.declare(occurrence, name);
5233            let family =
5234                crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5235                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5236                    {
5237                        collector.intern_node(family)
5238                    } else {
5239                        collector.intern_embedded(
5240                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5241                        )
5242                    }
5243                });
5244            (declaration, family)
5245        };
5246        self.source_declarations.insert(declaration);
5247        if !self.source_properties.borrow().contains_key(&declaration) {
5248            let context = crate::source_context::capture_declaration_context(
5249                node,
5250                unit,
5251                self.source,
5252                &mut self.preprocessor_context_cache,
5253            );
5254            let (exhaustive_family, displaced_namespace_closing_brace) = {
5255                let mut collector = self.source_collector.borrow_mut();
5256                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5257                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5258                    {
5259                        collector.intern_node(family)
5260                    } else {
5261                        collector.intern_embedded(
5262                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5263                        )
5264                    }
5265                });
5266                let displaced_namespace_closing_brace =
5267                    context.displaced_namespace_closing_brace.map(|brace| {
5268                        if brace.id() == self.primary_root_id
5269                            || self.primary_ancestry.contains(brace)
5270                        {
5271                            collector.intern_node(brace)
5272                        } else {
5273                            collector.intern_embedded(
5274                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5275                            )
5276                        }
5277                    });
5278                (exhaustive_family, displaced_namespace_closing_brace)
5279            };
5280            let guard_requirements = context.guard_requirements;
5281            let callable_guards = context.callable_guards;
5282            let callable_activation = context.callable_activation;
5283            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5284            let flattened_macro_namespace = context.flattened_macro_namespace;
5285            self.source_properties
5286                .borrow_mut()
5287                .entry(declaration)
5288                .or_insert_with(|| {
5289                    let mut fact = crate::source_facts::capture_declaration(
5290                        declaration,
5291                        node,
5292                        unit,
5293                        self.source,
5294                        ancestry,
5295                    );
5296                    self.capture_source_dependencies(&mut fact, node, unit);
5297                    fact.occurrence_role =
5298                        crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5299                    fact.conditional_family = family;
5300                    fact.guard_requirements = guard_requirements;
5301                    fact.callable_guards = callable_guards;
5302                    fact.callable_activation = callable_activation;
5303                    fact.callable_guard_completion_byte = callable_guard_completion_byte;
5304                    fact.exhaustive_conditional_family = exhaustive_family;
5305                    fact.flattened_macro_namespace = flattened_macro_namespace;
5306                    fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5307                    self.apply_active_source_context(&mut fact);
5308                    fact
5309                });
5310        }
5311        let links = self.source_links.entry(unit.clone()).or_default();
5312        if !links.contains(&declaration) {
5313            links.push(declaration);
5314        }
5315    }
5316
5317    fn link_source_range(
5318        &mut self,
5319        ancestry: &ParentIndex<'_>,
5320        unit: &CodeUnit,
5321        range: Range,
5322        evidence: Option<Node<'_>>,
5323    ) {
5324        let declaration = {
5325            let mut collector = self.source_collector.borrow_mut();
5326            let occurrence =
5327                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5328            let name = evidence
5329                .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5330                .map(|name| {
5331                    if self.primary_ancestry.contains(name) {
5332                        collector.intern_node(name)
5333                    } else {
5334                        collector.intern_embedded(
5335                            brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5336                        )
5337                    }
5338                });
5339            collector.declare(occurrence, name)
5340        };
5341        self.link_source_declaration_facts(
5342            unit,
5343            declaration,
5344            evidence.map(|node| (ancestry, node)),
5345        );
5346    }
5347
5348    /// [`Self::link_source_range`] with the name occurrence interned from the
5349    /// subspan that writes it instead of derived from an evidence node. A live
5350    /// evidence node still supplies the declaration's source facts.
5351    fn link_source_name_range(
5352        &mut self,
5353        unit: &CodeUnit,
5354        range: Range,
5355        name_range: Range,
5356        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5357    ) {
5358        let declaration = {
5359            let mut collector = self.source_collector.borrow_mut();
5360            let occurrence =
5361                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5362            let name =
5363                collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5364            collector.declare(occurrence, Some(name))
5365        };
5366        self.link_source_declaration_facts(unit, declaration, evidence);
5367    }
5368
5369    /// Record `declaration` for `unit` and, when a live evidence node exists,
5370    /// the source facts it supplies: conditional family, guards, context and
5371    /// dependency names. Shared by both occurrence-naming paths so a name
5372    /// subspan never costs a declaration its facts (#3093).
5373    fn link_source_declaration_facts(
5374        &mut self,
5375        unit: &CodeUnit,
5376        declaration: SourceDeclarationId,
5377        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5378    ) {
5379        let family = evidence.and_then(|(_, node)| {
5380            let mut collector = self.source_collector.borrow_mut();
5381            crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5382                if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5383                    collector.intern_node(family)
5384                } else {
5385                    collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5386                        family,
5387                    ))
5388                }
5389            })
5390        });
5391        self.source_declarations.insert(declaration);
5392        if let Some((ancestry, node)) = evidence
5393            && !self.source_properties.borrow().contains_key(&declaration)
5394        {
5395            let context = crate::source_context::capture_declaration_context(
5396                node,
5397                unit,
5398                self.source,
5399                &mut self.preprocessor_context_cache,
5400            );
5401            let (exhaustive_family, displaced_namespace_closing_brace) = {
5402                let mut collector = self.source_collector.borrow_mut();
5403                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5404                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5405                    {
5406                        collector.intern_node(family)
5407                    } else {
5408                        collector.intern_embedded(
5409                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5410                        )
5411                    }
5412                });
5413                let displaced_namespace_closing_brace =
5414                    context.displaced_namespace_closing_brace.map(|brace| {
5415                        if brace.id() == self.primary_root_id
5416                            || self.primary_ancestry.contains(brace)
5417                        {
5418                            collector.intern_node(brace)
5419                        } else {
5420                            collector.intern_embedded(
5421                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5422                            )
5423                        }
5424                    });
5425                (exhaustive_family, displaced_namespace_closing_brace)
5426            };
5427            let guard_requirements = context.guard_requirements;
5428            let callable_guards = context.callable_guards;
5429            let callable_activation = context.callable_activation;
5430            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5431            let flattened_macro_namespace = context.flattened_macro_namespace;
5432            let mut fact = crate::source_facts::capture_declaration(
5433                declaration,
5434                node,
5435                unit,
5436                self.source,
5437                ancestry,
5438            );
5439            self.capture_source_dependencies(&mut fact, node, unit);
5440            fact.occurrence_role =
5441                crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5442            fact.conditional_family = family;
5443            fact.guard_requirements = guard_requirements;
5444            fact.callable_guards = callable_guards;
5445            fact.callable_activation = callable_activation;
5446            fact.callable_guard_completion_byte = callable_guard_completion_byte;
5447            fact.exhaustive_conditional_family = exhaustive_family;
5448            fact.flattened_macro_namespace = flattened_macro_namespace;
5449            fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5450            self.apply_active_source_context(&mut fact);
5451            self.source_properties
5452                .borrow_mut()
5453                .insert(declaration, fact);
5454        }
5455        let links = self.source_links.entry(unit.clone()).or_default();
5456        if !links.contains(&declaration) {
5457            links.push(declaration);
5458        }
5459    }
5460
5461    fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5462        let declaration = *self
5463            .source_links
5464            .get(&unit)
5465            .and_then(|links| links.last())
5466            .expect("signature metadata follows its source declaration");
5467        let metadata_ordinal = self
5468            .parsed
5469            .add_signature_with_metadata(unit.clone(), metadata);
5470        let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5471            declaration,
5472            unit,
5473            metadata_ordinal,
5474        };
5475        if !self.parsed.source_declaration_metadata.contains(&link) {
5476            self.parsed.source_declaration_metadata.push(link);
5477        }
5478    }
5479
5480    /// Records `code_unit` with the answers the walk carries forward, then adds
5481    /// it to the parse product.
5482    ///
5483    /// Every declaration this walk publishes goes through this family, so the
5484    /// carried-forward answers see each one exactly once: the minted declaration
5485    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
5486    fn add_declaration(
5487        &mut self,
5488        ancestry: &ParentIndex<'_>,
5489        code_unit: CodeUnit,
5490        node: Node<'_>,
5491        parent: Option<CodeUnit>,
5492        top_level: Option<CodeUnit>,
5493    ) {
5494        self.note_declaration(&code_unit);
5495        self.link_source_declaration(ancestry, &code_unit, node);
5496        let source = self.source;
5497        self.parsed
5498            .add_code_unit(code_unit, node, source, parent, top_level);
5499    }
5500
5501    /// Range-based form of [`Self::add_declaration`].
5502    fn add_declaration_with_range(
5503        &mut self,
5504        ancestry: &ParentIndex<'_>,
5505        code_unit: CodeUnit,
5506        range: Range,
5507        evidence: Option<Node<'_>>,
5508        parent: Option<CodeUnit>,
5509        top_level: Option<CodeUnit>,
5510    ) {
5511        self.note_declaration(&code_unit);
5512        self.link_source_range(ancestry, &code_unit, range, evidence);
5513        self.parsed
5514            .add_code_unit_with_range(code_unit, range, parent, top_level);
5515    }
5516
5517    /// Name-subspan form of [`Self::add_declaration_with_range`]: the name
5518    /// occurrence is the recorded subspan rather than a token derived from an
5519    /// evidence node (#4292). A live `evidence` node, when the caller has one,
5520    /// still supplies the declaration's source facts; an index-recovered
5521    /// declaration has none.
5522    fn add_declaration_with_name_range(
5523        &mut self,
5524        code_unit: CodeUnit,
5525        range: Range,
5526        name_range: Range,
5527        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5528        parent: Option<CodeUnit>,
5529        top_level: Option<CodeUnit>,
5530    ) {
5531        self.note_declaration(&code_unit);
5532        self.link_source_name_range(&code_unit, range, name_range, evidence);
5533        self.parsed
5534            .add_code_unit_with_range(code_unit, range, parent, top_level);
5535    }
5536
5537    /// Deferred-replacement form of [`Self::add_declaration`].
5538    fn replace_declaration_deferred(
5539        &mut self,
5540        ancestry: &ParentIndex<'_>,
5541        code_unit: CodeUnit,
5542        node: Node<'_>,
5543        parent: Option<CodeUnit>,
5544        top_level: Option<CodeUnit>,
5545    ) {
5546        self.note_replaced_declaration(&code_unit);
5547        self.link_source_declaration(ancestry, &code_unit, node);
5548        let source = self.source;
5549        self.parsed
5550            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5551    }
5552
5553    /// Range-based form of [`Self::replace_declaration_deferred`].
5554    fn replace_declaration_with_range_deferred(
5555        &mut self,
5556        ancestry: &ParentIndex<'_>,
5557        code_unit: CodeUnit,
5558        range: Range,
5559        evidence: Option<Node<'_>>,
5560        parent: Option<CodeUnit>,
5561        top_level: Option<CodeUnit>,
5562    ) {
5563        self.note_replaced_declaration(&code_unit);
5564        self.link_source_range(ancestry, &code_unit, range, evidence);
5565        self.parsed
5566            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5567    }
5568
5569    /// Notes one declaration about to enter the parse product.
5570    ///
5571    /// A declaration the product already holds is not a creation, so an open
5572    /// recovery capture ignores it -- which is the membership test the set
5573    /// difference it replaces performed. A creation inside a nested recovery
5574    /// belongs to the recoveries around it too, so every open capture takes it.
5575    fn note_declaration(&mut self, code_unit: &CodeUnit) {
5576        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5577            for capture in &mut self.recovery_captures {
5578                if capture.removed_pre_existing.contains(code_unit) {
5579                    continue;
5580                }
5581                if capture.created_units.insert(code_unit.clone()) {
5582                    capture.created.push(code_unit.clone());
5583                }
5584            }
5585        }
5586    }
5587
5588    /// Notes one declaration about to replace an existing one.
5589    ///
5590    /// A deferred replacement of a declaration that already owns children
5591    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
5592    /// their source mounts and recovery captures must track the removal.
5593    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5594        // Forward and definition occurrences remain real navigation alternatives.
5595        // Only identities removed with replaced children lose their mounts.
5596        for removed in self.declarations_a_replacement_removes(code_unit) {
5597            self.source_links.remove(&removed);
5598        }
5599        let removes_children = self.parsed.contains_declaration(code_unit)
5600            && self
5601                .parsed
5602                .children
5603                .get(code_unit)
5604                .is_some_and(|children| !children.is_empty());
5605        if removes_children && !self.recovery_captures.is_empty() {
5606            let removed = self.declarations_a_replacement_removes(code_unit);
5607            for capture in &mut self.recovery_captures {
5608                for unit in &removed {
5609                    // A declaration this capture watched being created is
5610                    // its own; one it did not is a declaration that was
5611                    // already there when the capture opened, so creating it
5612                    // again is a restoration and not a mint.
5613                    if !capture.created_units.contains(unit) {
5614                        capture.removed_pre_existing.insert(unit.clone());
5615                    }
5616                }
5617            }
5618        }
5619        self.note_declaration(code_unit);
5620    }
5621
5622    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
5623    /// when `code_unit` is replaced: its children, transitively.
5624    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5625        let mut removed = Vec::new();
5626        let mut seen = HashSet::default();
5627        let mut pending: Vec<CodeUnit> = self
5628            .parsed
5629            .children
5630            .get(code_unit)
5631            .cloned()
5632            .unwrap_or_default();
5633        while let Some(unit) = pending.pop() {
5634            if !seen.insert(unit.clone()) {
5635                continue;
5636            }
5637            if let Some(children) = self.parsed.children.get(&unit) {
5638                pending.extend(children.iter().cloned());
5639            }
5640            removed.push(unit);
5641        }
5642        removed
5643    }
5644
5645    fn visit_recovered_function_like_export_class<'tree>(
5646        &mut self,
5647        node: Node<'tree>,
5648        recovered: RecoveredFunctionLikeExportClass,
5649        scope: &ScopeInfo,
5650        stack: &mut Vec<CppWork<'tree>>,
5651        ancestry: &ParentIndex<'tree>,
5652    ) {
5653        let RecoveredFunctionLikeExportClass {
5654            name,
5655            range,
5656            raw_supertypes,
5657            base_facts,
5658            member_outcome,
5659        } = recovered;
5660        let class_unit = self.visit_named_class_like_shape(
5661            node,
5662            name,
5663            None,
5664            true,
5665            Some(range),
5666            raw_supertypes,
5667            base_facts,
5668            scope,
5669            stack,
5670            ancestry,
5671        );
5672        self.parsed
5673            .record_materialization(MaterializationRecord::RecoveredDeclaration {
5674                recovery: range,
5675                unit: class_unit.clone(),
5676            });
5677        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5678            && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5679                tree.root_node(),
5680                class_unit.identifier(),
5681                self.source,
5682            )
5683        {
5684            self.visit_recovered_fragment_constructor(
5685                constructor_range,
5686                body,
5687                node,
5688                &class_unit,
5689                scope,
5690                ancestry,
5691            );
5692        }
5693        if let Some(outcome) = member_outcome {
5694            self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5695        }
5696    }
5697
5698    fn visit_function_like_export_class_pair<'tree>(
5699        &mut self,
5700        node: Node<'tree>,
5701        scope: &ScopeInfo,
5702        stack: &mut Vec<CppWork<'tree>>,
5703        ancestry: &ParentIndex<'tree>,
5704    ) -> bool {
5705        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5706            return false;
5707        };
5708        let member_outcome = self
5709            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5710        // A malformed class body can escape into several following siblings
5711        // before the next export-macro class head appears. Inspect siblings in
5712        // order and stop at the first envelope that contains such a head. One
5713        // envelope can contain several following classes, all recovered in a
5714        // single bounded traversal.
5715        let mut displaced = node.next_named_sibling();
5716        while let Some(candidate) = displaced {
5717            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5718                break;
5719            }
5720            displaced = candidate.next_named_sibling();
5721        }
5722        // The adjacent initializer_list proves the class body envelope, but
5723        // its children are expression-shaped rather than declaration-shaped.
5724        // Visit only separately recovered members; ordinary callable
5725        // definitions remain available from their out-of-line declarations.
5726        let range = recovered.range;
5727        self.visit_recovered_function_like_export_class(
5728            node,
5729            RecoveredFunctionLikeExportClass {
5730                name: recovered.name,
5731                range,
5732                raw_supertypes: recovered.raw_supertypes,
5733                base_facts: recovered.base_facts,
5734                member_outcome,
5735            },
5736            scope,
5737            stack,
5738            ancestry,
5739        );
5740        self.consumed_fragment_regions
5741            .push((node.start_byte(), range.end_byte));
5742        true
5743    }
5744
5745    fn visit_embedded_function_like_export_classes<'tree>(
5746        &mut self,
5747        node: Node<'tree>,
5748        scope: &ScopeInfo,
5749        stack: &mut Vec<CppWork<'tree>>,
5750        ancestry: &ParentIndex<'tree>,
5751    ) -> bool {
5752        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5753        let found = !recovered_classes.is_empty();
5754        for recovered in recovered_classes {
5755            let member_outcome = self.reparse_fragmented_export_class_members(
5756                &recovered.fragmented_body,
5757                &recovered.name,
5758            );
5759            self.visit_recovered_function_like_export_class(
5760                node,
5761                RecoveredFunctionLikeExportClass {
5762                    name: recovered.name,
5763                    range: recovered.range,
5764                    raw_supertypes: Some(recovered.raw_supertypes),
5765                    base_facts: Some(recovered.base_facts),
5766                    member_outcome,
5767                },
5768                scope,
5769                stack,
5770                ancestry,
5771            );
5772        }
5773        found
5774    }
5775
5776    /// Walk `node`'s container, answering every ancestor question from
5777    /// `ancestry`.
5778    ///
5779    /// `ancestry` must index the tree `node` belongs to. The caller owns it
5780    /// because one tree can be walked more than once -- a header's C and C++
5781    /// readings are the same tree under different tag semantics -- and the
5782    /// parent relation is a property of the tree, not of the reading.
5783    #[allow(clippy::too_many_arguments)]
5784    pub fn visit_container<'tree>(
5785        &mut self,
5786        node: Node<'tree>,
5787        ancestry: &ParentIndex<'tree>,
5788        package_name: &str,
5789        module: Option<CodeUnit>,
5790        class_unit: Option<CodeUnit>,
5791        template_signature: Option<String>,
5792        visible_using_namespaces: Vec<String>,
5793        visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
5794    ) {
5795        let scope = ScopeInfo {
5796            package_name: package_name.to_string(),
5797            module,
5798            class_unit,
5799            template_signature,
5800            template_metadata: None,
5801            declarations_are_fields: false,
5802            recovered_specialization_member_scope: false,
5803            visible_using_namespaces,
5804            local_block: None,
5805            visible_using_declarations,
5806        };
5807        // The work loop dispatches a container's children, never the container
5808        // itself, so an `ERROR` container that is the seed does not reach the
5809        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
5810        // breaks the parse leaves the whole translation unit as that seed
5811        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
5812        // macro-error-class recovery reads.
5813        if node.is_error() {
5814            self.visit_object_macro_error_classes(node, &scope);
5815        }
5816        self.run_container_work(node, scope, ancestry);
5817        self.drain_partitioned_regions();
5818    }
5819
5820    /// Drain reparsed containers after their primary walk has indexed them.
5821    /// Recovered nodes retain their own tree and source context while they are
5822    /// visited; only then are their byte ranges marked consumed so a shared
5823    /// primary event stream does not visit the same declarations twice.
5824    fn drain_partitioned_regions(&mut self) {
5825        let mut consumed = Vec::new();
5826        while let Some(CppPartitionedRegion {
5827            tree,
5828            range,
5829            scope,
5830            recovery,
5831            source_context,
5832        }) = self.partitioned_regions.pop()
5833        {
5834            let root = tree.root_node();
5835            let container = root
5836                .descendant_for_byte_range(range.start, range.end)
5837                .expect("the queued container belongs to this tree");
5838            assert_eq!(container.byte_range(), range);
5839            let ancestry = self.recovery_ancestry(root);
5840            self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5841                visitor.run_container_work(container, scope, &ancestry)
5842            });
5843            // A recovered walk may enqueue another partition inside this
5844            // region. Delay the outer claim until all nested regions have
5845            // drained, or their nodes would be skipped as already consumed.
5846            consumed.push((range.start, range.end));
5847        }
5848        self.consumed_fragment_regions.extend(consumed);
5849    }
5850
5851    /// Whether a work node lies entirely inside a byte region consumed by a
5852    /// fragmented export-class recovery (#938); such nodes were already indexed
5853    /// as members of the recovered class by the region reparse.
5854    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5855        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5856    }
5857
5858    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
5859    /// candidate a recovery is about to reparse but has not yet turned into a
5860    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
5861    /// prior structured recovery.
5862    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5863        self.consumed_fragment_regions
5864            .iter()
5865            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5866    }
5867
5868    /// Drive the container work loop from an explicit seed scope to completion. The
5869    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
5870    /// stays alive for the whole traversal.
5871    ///
5872    /// Every ancestor question this walk asks is answered from `ancestry`, which
5873    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
5874    /// node's position in the tree, which made a generated header with thousands
5875    /// of top-level declarations quadratic (#2361). The index is the caller's
5876    /// because it outlives any one walk: the file's tree is walked twice when a
5877    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
5878    /// builds its own index for its own tree.
5879    fn run_container_work<'tree>(
5880        &mut self,
5881        node: Node<'tree>,
5882        scope: ScopeInfo,
5883        ancestry: &ParentIndex<'tree>,
5884    ) {
5885        self.drain_cpp_work(
5886            vec![CppWork::Container(CppContainer { node, scope })],
5887            ancestry,
5888        );
5889    }
5890
5891    /// The work loop itself, from whatever seed the caller built.
5892    ///
5893    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
5894    /// that must walk only part of a reparsed tree seeds it with the sibling
5895    /// range it may walk instead, which keeps the `using namespace X;` scope
5896    /// accumulation `advance_cpp_siblings` performs.
5897    fn drain_cpp_work<'tree>(
5898        &mut self,
5899        mut stack: Vec<CppWork<'tree>>,
5900        ancestry: &ParentIndex<'tree>,
5901    ) {
5902        while let Some(work) = stack.pop() {
5903            match work {
5904                CppWork::Container(container) => {
5905                    push_cpp_container_work(container.node, container.scope, &mut stack);
5906                }
5907                CppWork::Siblings(siblings) => {
5908                    advance_cpp_siblings(siblings, self.source, &mut stack);
5909                }
5910                CppWork::Node(work) => {
5911                    if self.node_is_inside_consumed_fragment(work.node) {
5912                        continue;
5913                    }
5914                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5915                }
5916            }
5917        }
5918    }
5919
5920    /// Drain nodes selected by a bounded recovery walk without filtering them
5921    /// against consumed regions from the primary tree.
5922    fn drain_cpp_recovery_work<'tree>(
5923        &mut self,
5924        mut stack: Vec<CppWork<'tree>>,
5925        ancestry: &ParentIndex<'tree>,
5926    ) {
5927        while let Some(work) = stack.pop() {
5928            match work {
5929                CppWork::Container(container) => {
5930                    push_cpp_container_work(container.node, container.scope, &mut stack);
5931                }
5932                CppWork::Siblings(siblings) => {
5933                    advance_cpp_siblings(siblings, self.source, &mut stack);
5934                }
5935                CppWork::Node(work) => {
5936                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5937                }
5938            }
5939        }
5940    }
5941
5942    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
5943    /// it only when the entire region is member-shaped. This validation must happen
5944    /// before registering the recovered class because a rejected speculative range
5945    /// must not leak into the ordinary recovery path.
5946    fn reparse_fragmented_export_class_members(
5947        &self,
5948        fragmented: &FragmentedExportBody,
5949        class_name: &str,
5950    ) -> Option<FragmentedExportMembers> {
5951        if fragmented.reparse_start >= fragmented.reparse_end {
5952            return None;
5953        }
5954        let tree = cpp_reparse_fragmented_class_body(
5955            self.source,
5956            fragmented.reparse_start,
5957            fragmented.reparse_end,
5958        )?;
5959        if cpp_reparsed_members_are_indexable_for_class(
5960            tree.root_node(),
5961            self.source,
5962            Some(class_name),
5963        ) {
5964            return Some(FragmentedExportMembers::Complete(tree));
5965        }
5966        let has_conditional_constructor = {
5967            let root = tree.root_node();
5968            let mut cursor = root.walk();
5969            root.named_children(&mut cursor).any(|child| {
5970                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5971            })
5972        };
5973        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5974    }
5975
5976    /// Index an already validated fragmented body as members of `class_unit`. The
5977    /// region reparse keeps each member's exact original byte and line positions.
5978    fn visit_fragmented_export_class_members(
5979        &mut self,
5980        origin: Node<'_>,
5981        outcome: FragmentedExportMembers,
5982        class_unit: CodeUnit,
5983        scope: &ScopeInfo,
5984    ) -> bool {
5985        self.with_recovery_source_context(origin, |visitor| {
5986            visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5987        })
5988    }
5989
5990    fn visit_fragmented_export_class_members_in_context(
5991        &mut self,
5992        outcome: FragmentedExportMembers,
5993        class_unit: CodeUnit,
5994        scope: &ScopeInfo,
5995    ) -> bool {
5996        let (tree, complete) = match outcome {
5997            FragmentedExportMembers::Complete(tree) => (tree, true),
5998            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5999        };
6000        let root = tree.root_node();
6001        let class_name = class_unit.identifier().to_string();
6002        let member_scope = ScopeInfo {
6003            // A recovered export-macro class may borrow its namespace from an
6004            // earlier forward declaration even when the malformed node itself
6005            // sits at file scope. Use the recovered class identity as the
6006            // authoritative package for reparsed members as well.
6007            package_name: class_unit.package_name().to_string(),
6008            module: scope.module.clone(),
6009            class_unit: Some(class_unit),
6010            template_signature: scope.template_signature.clone(),
6011            template_metadata: None,
6012            declarations_are_fields: true,
6013            recovered_specialization_member_scope: false,
6014            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6015            local_block: None,
6016            visible_using_declarations: scope.visible_using_declarations.clone(),
6017        };
6018        let reparsed_ancestry = self.recovery_ancestry(root);
6019        if !complete {
6020            // A conditional beginning immediately after an access label can
6021            // fragment one constructor declaration while leaving the rest of
6022            // the class body as unsafe statement soup. Recover only that
6023            // structurally proven constructor and leave the outer-tree
6024            // siblings unconsumed for their ordinary walk.
6025            let mut cursor = root.walk();
6026            let constructors = root
6027                .named_children(&mut cursor)
6028                .filter_map(|child| {
6029                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
6030                })
6031                .collect::<Vec<_>>();
6032            // The reparsed region is its own tree, so this drain walks it with
6033            // its own parent index.
6034            for constructor in constructors {
6035                let mut stack = Vec::new();
6036                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
6037                self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
6038            }
6039            return false;
6040        }
6041        let mut cursor = root.walk();
6042        let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6043        for member in members.iter().copied().filter(|member| {
6044            cpp_reparsed_defaulted_constructor_is_indexable(
6045                *member,
6046                &members,
6047                Some(class_name.as_str()),
6048                self.source,
6049            )
6050        }) {
6051            self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6052        }
6053        for member in members.iter().copied().filter(|member| {
6054            cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6055        }) {
6056            self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6057        }
6058        // The reparsed region is its own tree, so this walk indexes it itself.
6059        self.run_container_work(root, member_scope, &reparsed_ancestry);
6060        true
6061    }
6062
6063    fn visit_recovered_fragment_constructor<'tree>(
6064        &mut self,
6065        range: std::ops::Range<usize>,
6066        constructor_body: Node<'tree>,
6067        class_declaration: Node<'tree>,
6068        class_unit: &CodeUnit,
6069        scope: &ScopeInfo,
6070        ancestry: &ParentIndex<'tree>,
6071    ) {
6072        self.with_recovery_source_context(class_declaration, |visitor| {
6073            visitor.visit_recovered_fragment_constructor_in_context(
6074                range,
6075                constructor_body,
6076                class_declaration,
6077                class_unit,
6078                scope,
6079                ancestry,
6080            );
6081        });
6082    }
6083
6084    fn visit_recovered_fragment_constructor_in_context<'tree>(
6085        &mut self,
6086        range: std::ops::Range<usize>,
6087        constructor_body: Node<'tree>,
6088        class_declaration: Node<'tree>,
6089        class_unit: &CodeUnit,
6090        scope: &ScopeInfo,
6091        ancestry: &ParentIndex<'tree>,
6092    ) {
6093        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6094            return;
6095        };
6096        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6097            tree.root_node(),
6098            range.start,
6099            class_unit.identifier(),
6100            self.source,
6101        ) else {
6102            return;
6103        };
6104        let member_scope = ScopeInfo {
6105            package_name: class_unit.package_name().to_string(),
6106            module: scope.module.clone(),
6107            class_unit: Some(class_unit.clone()),
6108            template_signature: scope.template_signature.clone(),
6109            template_metadata: None,
6110            declarations_are_fields: true,
6111            recovered_specialization_member_scope: false,
6112            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6113            local_block: None,
6114            visible_using_declarations: scope.visible_using_declarations.clone(),
6115        };
6116        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6117        else {
6118            return;
6119        };
6120        debug_assert_eq!(function.name, class_unit.identifier());
6121        let code_unit = function.code_unit(self.file.clone());
6122        let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6123        self.add_declaration_with_range(
6124            &recovered_ancestry,
6125            code_unit.clone(),
6126            Range {
6127                start_byte: function_declarator.start_byte(),
6128                end_byte: constructor_body.end_byte(),
6129                start_line: function_declarator.start_position().row + 1,
6130                end_line: constructor_body.end_position().row + 1,
6131            },
6132            Some(function_declarator),
6133            None,
6134            None,
6135        );
6136        let declaration = *self.source_links[&code_unit]
6137            .last()
6138            .expect("recovered constructor source");
6139        {
6140            let mut properties = self.source_properties.borrow_mut();
6141            let fact = properties
6142                .get_mut(&declaration)
6143                .expect("recovered constructor source fact");
6144            fact.occurrence_role =
6145                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6146            fact.callable_is_constructor = true;
6147        }
6148        self.add_signature_with_metadata(
6149            code_unit.clone(),
6150            cpp_signature_metadata(
6151                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6152                function_declarator,
6153                cpp_callable_linkage(class_declaration, self.source, ancestry),
6154                self.source,
6155                &recovered_ancestry,
6156            )
6157            .with_declaration_only(false),
6158        );
6159        self.parsed.add_child(class_unit.clone(), code_unit);
6160    }
6161
6162    fn visit_recovered_fragment_prefix_members<'tree>(
6163        &mut self,
6164        origin: Node<'_>,
6165        root: Node<'tree>,
6166        constructor_start: usize,
6167        class_unit: &CodeUnit,
6168        scope: &ScopeInfo,
6169        ancestry: &ParentIndex<'tree>,
6170    ) {
6171        self.with_recovery_source_context(origin, |visitor| {
6172            visitor.visit_recovered_fragment_prefix_members_in_context(
6173                root,
6174                constructor_start,
6175                class_unit,
6176                scope,
6177                ancestry,
6178            );
6179        });
6180    }
6181
6182    fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6183        &mut self,
6184        root: Node<'tree>,
6185        constructor_start: usize,
6186        class_unit: &CodeUnit,
6187        scope: &ScopeInfo,
6188        ancestry: &ParentIndex<'tree>,
6189    ) {
6190        let member_scope = ScopeInfo {
6191            package_name: class_unit.package_name().to_string(),
6192            module: scope.module.clone(),
6193            class_unit: Some(class_unit.clone()),
6194            template_signature: scope.template_signature.clone(),
6195            template_metadata: None,
6196            declarations_are_fields: true,
6197            recovered_specialization_member_scope: false,
6198            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6199            local_block: None,
6200            visible_using_declarations: scope.visible_using_declarations.clone(),
6201        };
6202        let mut root_cursor = root.walk();
6203        let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6204        let mut stack = vec![root];
6205        while let Some(current) = stack.pop() {
6206            if current.kind() == "comment" || current.start_byte() >= constructor_start {
6207                continue;
6208            }
6209            if current
6210                .parent()
6211                .is_some_and(|parent| same_node(parent, root))
6212                && cpp_reparsed_defaulted_constructor_is_indexable(
6213                    current,
6214                    &members,
6215                    Some(class_unit.identifier()),
6216                    self.source,
6217                )
6218            {
6219                self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6220                continue;
6221            }
6222            if current.end_byte() <= constructor_start
6223                && current.kind() != "translation_unit"
6224                && current.kind() != "labeled_statement"
6225                && current.kind() != "ERROR"
6226            {
6227                let mut work_stack = Vec::new();
6228                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6229                self.drain_cpp_recovery_work(work_stack, ancestry);
6230                continue;
6231            }
6232            if matches!(
6233                current.kind(),
6234                "translation_unit" | "labeled_statement" | "ERROR"
6235            ) {
6236                let mut cursor = current.walk();
6237                stack.extend(current.named_children(&mut cursor));
6238            }
6239        }
6240    }
6241
6242    fn visit_recovered_defaulted_constructor(
6243        &mut self,
6244        statement: Node<'_>,
6245        scope: &ScopeInfo,
6246        ancestry: &ParentIndex<'_>,
6247    ) {
6248        let Some(parent) = scope.class_unit.clone() else {
6249            return;
6250        };
6251        let Some(assignment) = statement.named_child(0) else {
6252            return;
6253        };
6254        let Some(call) = assignment.child_by_field_name("left") else {
6255            return;
6256        };
6257        let Some(function_name) = call.child_by_field_name("function") else {
6258            return;
6259        };
6260        let Some(arguments) = call.child_by_field_name("arguments") else {
6261            return;
6262        };
6263        if function_name.kind() != "identifier"
6264            || node_text(function_name, self.source) != parent.identifier()
6265            || arguments.kind() != "argument_list"
6266            || arguments.named_child_count() != 0
6267        {
6268            return;
6269        }
6270        let function = FunctionInfo {
6271            package_name: scope.package_name.clone(),
6272            owner: Some(CppMemberOwner::Unit(parent.clone())),
6273            name: parent.identifier().to_string(),
6274            signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6275        };
6276        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6277        if self.parsed.contains_declaration(&code_unit) {
6278            return;
6279        }
6280        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6281        let metadata = SignatureMetadata::new(
6282            normalize_cpp_whitespace(node_text(statement, self.source)),
6283            Vec::new(),
6284        )
6285        .with_declaration_only(true)
6286        .with_callable_arity(CallableArity::exact(0))
6287        .with_overload_discriminator(Some(
6288            brokk_bifrost_core::analyzer::tree_walk::overload_discriminator(Vec::<String>::new()),
6289        ))
6290        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6291        // The reparse proves class ownership, but carries no original class
6292        // access ladder. Preserve unknown access rather than guessing it.
6293        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6294        self.add_signature_with_metadata(code_unit.clone(), metadata);
6295        if let Some(declaration) = self
6296            .source_links
6297            .get(&code_unit)
6298            .and_then(|links| links.last())
6299            .copied()
6300        {
6301            let mut properties = self.source_properties.borrow_mut();
6302            let fact = properties
6303                .get_mut(&declaration)
6304                .expect("recovered defaulted constructor source fact");
6305            fact.occurrence_role =
6306                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6307            fact.callable_is_constructor = true;
6308        }
6309        self.parsed.add_child(parent, code_unit);
6310    }
6311
6312    /// Recover a constructor declaration a statement-context class-body
6313    /// reparse read as a call or parenthesized variable declarator. The
6314    /// statement is validated and its parameters read from a synthetic class
6315    /// context, where the constructor declaration parses as a real function
6316    /// declarator; the declaration itself is minted from the original
6317    /// statement node, so every range keeps its original byte positions.
6318    fn visit_bare_constructor_expression<'tree>(
6319        &mut self,
6320        statement: Node<'tree>,
6321        scope: &ScopeInfo,
6322        ancestry: &ParentIndex<'tree>,
6323    ) -> bool {
6324        let Some(parent) = scope.class_unit.clone() else {
6325            return false;
6326        };
6327        const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6328        let region = &self.source[statement.start_byte()..statement.end_byte()];
6329        let parsed = crate::graph::syntax::parse_sentinel(
6330            PREFIX,
6331            crate::graph::syntax::SentinelInput::region(region),
6332            " };",
6333        );
6334        let (synthetic, tree) = (parsed.source, parsed.tree);
6335        let root = tree.root_node();
6336        if root.has_error() {
6337            return false;
6338        }
6339        let mut declarator = None;
6340        let mut stack = vec![root];
6341        while let Some(current) = stack.pop() {
6342            if current.kind() == "function_declarator" {
6343                if declarator.is_some() {
6344                    return false;
6345                }
6346                declarator = Some(current);
6347                continue;
6348            }
6349            let mut cursor = current.walk();
6350            stack.extend(current.named_children(&mut cursor));
6351        }
6352        let Some(declarator) = declarator else {
6353            return false;
6354        };
6355        let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6356            return false;
6357        };
6358        let signature = cpp_parameter_signature(parameters_node, &synthetic);
6359        let mut parameter_labels = Vec::new();
6360        let mut parameter_cursor = parameters_node.walk();
6361        for parameter in parameters_node.named_children(&mut parameter_cursor) {
6362            if matches!(
6363                parameter.kind(),
6364                "parameter_declaration" | "optional_parameter_declaration"
6365            ) && let Some(name) = parameter
6366                .child_by_field_name("declarator")
6367                .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6368            {
6369                parameter_labels.push(name);
6370            }
6371        }
6372        let arity = parameter_labels.len();
6373        let function = FunctionInfo {
6374            package_name: scope.package_name.clone(),
6375            owner: Some(CppMemberOwner::Unit(parent.clone())),
6376            name: parent.identifier().to_string(),
6377            signature,
6378        };
6379        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6380        if self.parsed.contains_declaration(&code_unit) {
6381            return true;
6382        }
6383        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6384        let metadata = SignatureMetadata::with_parameter_labels(
6385            normalize_cpp_whitespace(node_text(statement, self.source)),
6386            parameter_labels,
6387        )
6388        .with_declaration_only(true)
6389        .with_callable_arity(CallableArity::exact(arity))
6390        .with_overload_discriminator(Some(cpp_overload_discriminator(declarator, &synthetic)))
6391        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6392        // The reparse proves class ownership, but carries no original
6393        // class access ladder. Preserve unknown access rather than
6394        // guessing it.
6395        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6396        self.add_signature_with_metadata(code_unit.clone(), metadata);
6397        if let Some(declaration) = self
6398            .source_links
6399            .get(&code_unit)
6400            .and_then(|links| links.last())
6401            .copied()
6402        {
6403            let mut properties = self.source_properties.borrow_mut();
6404            let fact = properties
6405                .get_mut(&declaration)
6406                .expect("recovered constructor source fact");
6407            fact.occurrence_role =
6408                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6409            fact.callable_is_constructor = true;
6410        }
6411        self.parsed.add_child(parent, code_unit);
6412        true
6413    }
6414
6415    /// Capture ancestors only when a proven recovery enters another tree. The
6416    /// ordinary declaration driver never performs this additional ancestor walk.
6417    fn with_recovery_source_context<T>(
6418        &mut self,
6419        node: Node<'_>,
6420        walk: impl FnOnce(&mut Self) -> T,
6421    ) -> T {
6422        self.with_recovery_source_context_at(node, node, walk)
6423    }
6424
6425    /// [`Self::with_recovery_source_context`] for a recovery whose content
6426    /// begins inside `node` at `preprocessor_anchor`: the lexical owners come
6427    /// from `node`, the preprocessor facts from the anchor. The two differ when
6428    /// the malformed node swallowed a branch directive that precedes the
6429    /// recovered content.
6430    fn with_recovery_source_context_at<T>(
6431        &mut self,
6432        node: Node<'_>,
6433        preprocessor_anchor: Node<'_>,
6434        walk: impl FnOnce(&mut Self) -> T,
6435    ) -> T {
6436        use crate::graph::resolver::{
6437            enclosing_namespace_context, merge_preprocessor_guards,
6438            preprocessor_conditional_family_for_declaration_with_cache,
6439            preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6440        };
6441        let previous = self.active_source_context.take();
6442        let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6443        let inherited = (!primary).then_some(previous.as_ref()).flatten();
6444        let local_guards = preprocessor_guard_environment_with_cache(
6445            preprocessor_anchor,
6446            self.source,
6447            &mut self.preprocessor_context_cache,
6448        );
6449        let guards = match inherited {
6450            Some(outer) => outer
6451                .guards
6452                .as_ref()
6453                .zip(local_guards.as_ref())
6454                .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6455            None => local_guards,
6456        };
6457        let local_namespace = enclosing_namespace_context(node, self.source);
6458        let namespace = match (
6459            inherited.and_then(|outer| outer.child_namespace.as_ref()),
6460            local_namespace,
6461        ) {
6462            (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6463            (Some(outer), None) => Some(outer.clone()),
6464            (None, local) => local,
6465        };
6466        let outer_lexical_scope =
6467            inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6468        let mut local_owners = Vec::new();
6469        let mut parent = node.parent();
6470        while let Some(ancestor) = parent {
6471            if matches!(
6472                ancestor.kind(),
6473                "namespace_definition"
6474                    | "class_specifier"
6475                    | "struct_specifier"
6476                    | "union_specifier"
6477                    | "enum_specifier"
6478            ) && let Some(name) = ancestor.child_by_field_name("name")
6479            {
6480                local_owners.push(node_text(name, self.source).to_owned());
6481            }
6482            parent = ancestor.parent();
6483        }
6484        local_owners.reverse();
6485        let mut child_lexical_scope = outer_lexical_scope.clone();
6486        child_lexical_scope.extend(local_owners);
6487        if matches!(
6488            node.kind(),
6489            "namespace_definition"
6490                | "class_specifier"
6491                | "struct_specifier"
6492                | "union_specifier"
6493                | "enum_specifier"
6494        ) && let Some(name) = node.child_by_field_name("name")
6495        {
6496            child_lexical_scope.push(node_text(name, self.source).to_owned());
6497        }
6498        let child_namespace = if node.kind() == "namespace_definition" {
6499            node.child_by_field_name("name")
6500                .map(|name| {
6501                    let local = node_text(name, self.source);
6502                    namespace
6503                        .as_ref()
6504                        .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6505                })
6506                .or_else(|| namespace.clone())
6507        } else {
6508            namespace.clone()
6509        };
6510        let intern = |family: Node<'_>| {
6511            let mut collector = self.source_collector.borrow_mut();
6512            if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6513                collector.intern_node(family)
6514            } else {
6515                collector
6516                    .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6517            }
6518        };
6519        let family = preprocessor_conditional_family_node(preprocessor_anchor)
6520            .map(intern)
6521            .or_else(|| inherited.and_then(|outer| outer.family));
6522        let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6523            preprocessor_anchor,
6524            &mut self.preprocessor_context_cache,
6525        )
6526        .map(intern)
6527        .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6528        let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6529            preprocessor_anchor,
6530            self.source,
6531            &mut self.preprocessor_context_cache,
6532        );
6533        let callable_guard_completion_byte = match inherited {
6534            Some(outer) => local_completion
6535                .zip(outer.callable_guard_completion_byte)
6536                .map(|(local, outer)| local.max(outer)),
6537            None => local_completion,
6538        };
6539        self.active_source_context = Some(CppActiveSourceContext {
6540            guards,
6541            namespace: child_namespace.clone(),
6542            child_namespace,
6543            outer_lexical_scope: child_lexical_scope.clone(),
6544            child_lexical_scope,
6545            family,
6546            exhaustive_family,
6547            callable_guard_completion_byte,
6548        });
6549        let result = walk(self);
6550        self.active_source_context = previous;
6551        result
6552    }
6553
6554    fn visit_node<'tree>(
6555        &mut self,
6556        node: Node<'tree>,
6557        scope: &ScopeInfo,
6558        stack: &mut Vec<CppWork<'tree>>,
6559        ancestry: &ParentIndex<'tree>,
6560    ) {
6561        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6562            self.visit_node(node, &recovered_scope, stack, ancestry);
6563            return;
6564        }
6565        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6566            self.visit_node(node, &recovered_scope, stack, ancestry);
6567            return;
6568        }
6569        if scope.class_unit.is_some()
6570            && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6571        {
6572            self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6573            return;
6574        }
6575        // Fragmented-class recovery below may consume a malformed function
6576        // envelope before the ordinary kind dispatch runs. Recover any
6577        // export-macro class embedded in that envelope first; the strict class
6578        // head/base/body predicate is independent of which later recovery owns
6579        // the surrounding parser fragment.
6580        if node.kind() == "function_definition" && node.has_error() {
6581            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6582        }
6583        if let Some(FragmentedClassRecovery {
6584            declaration_node: class_node,
6585            name,
6586            raw_supertypes,
6587            body: fragmented,
6588        }) = fragmented_class_body(node, self.source)
6589        {
6590            let displaced_namespace_items =
6591                displaced_fragment_namespace_geometry(node, self.source)
6592                    .map(|boundary| boundary.namespace_items)
6593                    .unwrap_or_default();
6594            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6595            let mut class_stack = Vec::new();
6596            // When the full body cannot be safely reparsed, the original class
6597            // node still proves ownership for its parser-visible prefix.
6598            let parser_visible_body =
6599                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6600                    .then(|| cpp_body_node(class_node))
6601                    .flatten();
6602            let class_unit = self.visit_named_class_like_shape(
6603                class_node,
6604                name,
6605                parser_visible_body,
6606                true,
6607                Some(fragmented.class_range),
6608                Some(raw_supertypes),
6609                Some(extract_cpp_base_facts(class_node, self.source)),
6610                scope,
6611                &mut class_stack,
6612                ancestry,
6613            );
6614            let member_scope = ScopeInfo {
6615                package_name: class_unit.package_name().to_string(),
6616                module: scope.module.clone(),
6617                class_unit: Some(class_unit.clone()),
6618                template_signature: scope.template_signature.clone(),
6619                template_metadata: None,
6620                declarations_are_fields: true,
6621                recovered_specialization_member_scope: false,
6622                visible_using_namespaces: scope.visible_using_namespaces.clone(),
6623                local_block: None,
6624                visible_using_declarations: scope.visible_using_declarations.clone(),
6625            };
6626            let complete = outcome.is_some_and(|outcome| {
6627                self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6628            });
6629            if complete {
6630                self.consumed_fragment_regions
6631                    .push((node.start_byte(), fragmented.class_range.end_byte));
6632            } else {
6633                // A macro-constrained member can make the full body reparse
6634                // unsafe while tree-sitter still exposes later class members
6635                // as bounded siblings up to the displaced `}`/`;`. Keep the
6636                // structurally proven class/base declaration and re-own those
6637                // sibling nodes under it. They retain their original parser
6638                // nodes and exact ranges; the close boundary comes solely from
6639                // `fragmented_class_body`.
6640                // Template wrappers put the escaped members beside the
6641                // template rather than beside its malformed declaration.
6642                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6643                    if candidate.start_byte() >= fragmented.reparse_end {
6644                        break;
6645                    }
6646                    if cpp_fragment_sibling_is_class_member(
6647                        candidate,
6648                        fragmented.reparse_end,
6649                        self.source,
6650                    ) {
6651                        self.recovered_class_sibling_scopes
6652                            .insert(candidate.id(), member_scope.clone());
6653                    }
6654                }
6655                // The macro-declaration grammar keeps the class head and its
6656                // parser-visible prefix members inside the fragmented
6657                // container itself, where there is no separate body node for
6658                // `visit_named_class_like_shape` to seed. Walk the container
6659                // with the member scope so those prefix members are owned by
6660                // the class; the head's bare tokens mint nothing on their own.
6661                if class_node.kind() == "ERROR" && class_node.has_error() {
6662                    stack.push(CppWork::Container(CppContainer {
6663                        node: class_node,
6664                        scope: member_scope,
6665                    }));
6666                }
6667            }
6668            for item in displaced_namespace_items {
6669                self.recovered_class_sibling_scopes
6670                    .insert(item.id(), scope.clone());
6671            }
6672            stack.extend(class_stack);
6673            return;
6674        }
6675        if self.visit_folded_aggregate(node, scope) {
6676            return;
6677        }
6678        match node.kind() {
6679            "template_declaration" => {
6680                if let Some(recovered) =
6681                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
6682                {
6683                    let mut template_scope = scope.clone();
6684                    template_scope.template_signature =
6685                        cpp_template_signature(node, recovered.declaration_node, self.source);
6686                    template_scope.template_metadata =
6687                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6688                    let raw_supertypes =
6689                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
6690                    let mut class_stack = Vec::new();
6691                    let class_unit = self.visit_named_class_like_shape(
6692                        recovered.class_node,
6693                        recovered.name,
6694                        Some(recovered.body),
6695                        true,
6696                        Some(recovered.range),
6697                        raw_supertypes,
6698                        Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6699                        &template_scope,
6700                        &mut class_stack,
6701                        ancestry,
6702                    );
6703                    self.parsed.record_materialization(
6704                        MaterializationRecord::RecoveredDeclaration {
6705                            recovery: recovered.range,
6706                            unit: class_unit.clone(),
6707                        },
6708                    );
6709                    let member_scope = ScopeInfo {
6710                        package_name: template_scope.package_name.clone(),
6711                        module: template_scope.module.clone(),
6712                        class_unit: Some(class_unit.clone()),
6713                        template_signature: template_scope.template_signature.clone(),
6714                        template_metadata: None,
6715                        declarations_are_fields: true,
6716                        recovered_specialization_member_scope: recovered
6717                            .class_node
6718                            .child_by_field_name("name")
6719                            .is_some_and(|name| name.kind() == "template_type"),
6720                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6721                        local_block: None,
6722                        visible_using_declarations: template_scope
6723                            .visible_using_declarations
6724                            .clone(),
6725                    };
6726                    for tail_member in recovered.tail_members.into_iter().rev() {
6727                        stack.push(CppWork::Node(CppNodeWork {
6728                            node: tail_member,
6729                            scope: member_scope.clone(),
6730                        }));
6731                    }
6732                    stack.extend(class_stack);
6733                    for sibling in recovered.member_siblings {
6734                        self.recovered_class_sibling_scopes
6735                            .insert(sibling.id(), member_scope.clone());
6736                    }
6737                    return;
6738                }
6739                for index in (0..node.named_child_count()).rev() {
6740                    let Some(child) = node.named_child(index) else {
6741                        continue;
6742                    };
6743                    if matches!(
6744                        child.kind(),
6745                        "class_specifier"
6746                            | "struct_specifier"
6747                            | "union_specifier"
6748                            | "enum_specifier"
6749                            | "function_definition"
6750                            | "declaration"
6751                            | "field_declaration"
6752                            | "alias_declaration"
6753                            | "namespace_definition"
6754                            // `template <class T> template <class U> void
6755                            // Box<T>::take(U) {}`: an out-of-line member
6756                            // template nests one head inside another. The
6757                            // inner head is the member's own and replaces the
6758                            // signature head when the nested node is visited.
6759                            | "template_declaration"
6760                    ) {
6761                        let mut template_scope = scope.clone();
6762                        template_scope.template_signature =
6763                            cpp_template_signature(node, child, self.source);
6764                        template_scope.template_metadata =
6765                            cpp_template_metadata(node, child, self.source, ancestry);
6766                        if let Some(recovered) = recover_fragmented_partial_specialization(
6767                            node,
6768                            child,
6769                            self.source,
6770                            ancestry,
6771                        ) {
6772                            let code_unit = self.visit_named_class_like_shape(
6773                                recovered.declaration_node,
6774                                recovered.name,
6775                                None,
6776                                true,
6777                                Some(recovered.range),
6778                                None,
6779                                None,
6780                                &template_scope,
6781                                stack,
6782                                ancestry,
6783                            );
6784                            self.parsed.record_materialization(
6785                                MaterializationRecord::RecoveredDeclaration {
6786                                    recovery: recovered.range,
6787                                    unit: code_unit.clone(),
6788                                },
6789                            );
6790                            let mut member_scope = template_scope.clone();
6791                            member_scope.class_unit = Some(code_unit);
6792                            member_scope.declarations_are_fields = true;
6793                            member_scope.recovered_specialization_member_scope = true;
6794                            for prefix_member in recovered.prefix_members.into_iter().rev() {
6795                                stack.push(CppWork::Node(CppNodeWork {
6796                                    node: prefix_member,
6797                                    scope: member_scope.clone(),
6798                                }));
6799                            }
6800                            for sibling in recovered.member_siblings {
6801                                self.recovered_class_sibling_scopes
6802                                    .insert(sibling.id(), member_scope.clone());
6803                            }
6804                            for following in recovered.following_declarations.into_iter().rev() {
6805                                stack.push(CppWork::Node(CppNodeWork {
6806                                    node: following,
6807                                    scope: scope.clone(),
6808                                }));
6809                            }
6810                            return;
6811                        }
6812                        stack.push(CppWork::Node(CppNodeWork {
6813                            node: child,
6814                            scope: template_scope,
6815                        }));
6816                    }
6817                }
6818            }
6819            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6820            "linkage_specification" => {
6821                if let Some(body) = cpp_body_node(node) {
6822                    // A linkage specification either wraps a direct declaration or a
6823                    // braced declaration_list. Visit the direct item itself so
6824                    // declaration recoveries still run on its original syntax node.
6825                    let work = if body.kind() == "declaration_list" {
6826                        CppWork::Container(CppContainer {
6827                            node: body,
6828                            scope: scope.clone(),
6829                        })
6830                    } else {
6831                        CppWork::Node(CppNodeWork {
6832                            node: body,
6833                            scope: scope.clone(),
6834                        })
6835                    };
6836                    stack.push(work);
6837                } else {
6838                    stack.push(CppWork::Container(CppContainer {
6839                        node,
6840                        scope: scope.clone(),
6841                    }));
6842                }
6843            }
6844            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6845                self.visit_class_like(node, scope, stack, ancestry)
6846            }
6847            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6848            // A bare namespace-begin sentinel can make tree-sitter promote the
6849            // wrapped declaration to an ERROR node instead of the usual bogus
6850            // function_definition envelope. Keep the recovery entry point on
6851            // the same structured path for both shapes; ordinary ERROR nodes
6852            // retain their declaration-preserving wrapper traversal when the
6853            // sentinel predicate does not match.
6854            "ERROR" => {
6855                // A standalone field-list invocation has no declaration wrapper.
6856                // Claim only an envelope consisting entirely of known invocations.
6857                if node.named_child_count() > 0
6858                    && node.children(&mut node.walk()).all(|child| {
6859                        child.kind() == "identifier"
6860                            && self
6861                                .object_macro_fields
6862                                .contains_key(node_text(child, self.source))
6863                    })
6864                    && self.visit_bare_object_macro_fields(node, scope)
6865                {
6866                    return;
6867                }
6868                self.visit_object_macro_error_classes(node, scope);
6869                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6870                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6871                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6872                        return;
6873                    }
6874                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6875                        return;
6876                    }
6877                    self.visit_macro_swallowed_function_declarations(node, scope);
6878                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
6879                    self.visit_stranded_class_members(node, scope, ancestry);
6880                    stack.push(CppWork::Container(CppContainer {
6881                        node,
6882                        scope: scope.clone(),
6883                    }));
6884                }
6885            }
6886            "declaration" => {
6887                if node.has_error() {
6888                    // Object-like macro lines before a function-like
6889                    // export-macro class head keep the head in a `declaration`
6890                    // rather than in an `ERROR` (#2924). It is the same head
6891                    // and the same following-sibling body, so it takes the same
6892                    // recovery; without it the head's macro invocation indexes
6893                    // as a field named after the macro.
6894                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6895                        return;
6896                    }
6897                }
6898                // The macro-declaration grammar reads a K&R prototype macro
6899                // line as a clean declaration with the name and the macro
6900                // displaced into `macro_modifier` slots, so the prototype
6901                // candidate gate runs on clean declarations too; the candidates
6902                // themselves remain the admission proof.
6903                self.visit_prototype_macro_declarations(node, scope, ancestry);
6904                if self.node_is_inside_consumed_fragment(node) {
6905                    // The whole declaration was a recovered K&R
6906                    // prototype macro invocation (issue #2932); the
6907                    // reparse above already indexed its Function.
6908                    // Falling through to the ordinary declaration
6909                    // visitor would additionally mint the swapped-field
6910                    // wreckage this recovery exists to replace.
6911                    return;
6912                }
6913                if scope.class_unit.is_some()
6914                    && scope.declarations_are_fields
6915                    && scope.recovered_specialization_member_scope
6916                    && let Some(alias_name) =
6917                        recovered_using_declaration_alias_name(node, self.source)
6918                {
6919                    self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6920                } else {
6921                    self.visit_declaration(
6922                        node,
6923                        scope,
6924                        scope.declarations_are_fields,
6925                        stack,
6926                        ancestry,
6927                    )
6928                }
6929            }
6930            // A K&R prototype macro invocation with a pointer return type and a
6931            // simple argument list parses with no ERROR node at all: tree-sitter
6932            // reads `T *name _(( args ));` as a multiplication of a type by a
6933            // call (`T * name_(...)`), wrapped in an `expression_statement`
6934            // that `has_error()` only because of a `MISSING "::"` inside it
6935            // (issue #2932). Nothing else mints a declaration from an
6936            // `expression_statement` at declaration scope today, so there is
6937            // no existing behavior to preserve here if this node is not the
6938            // K&R shape; the admission gate inside
6939            // `visit_prototype_macro_declarations` (exactly one clean function
6940            // prototype spanning the recovered run) is what keeps this arm
6941            // safe on an ordinary errorful expression statement that happens
6942            // to contain nested parentheses.
6943            "expression_statement" => {
6944                if node.has_error() {
6945                    self.visit_prototype_macro_declarations(node, scope, ancestry);
6946                }
6947            }
6948            "field_declaration" => {
6949                // A class-body K&R prototype macro line is the same
6950                // `macro_modifier` shape as a file-scope one, so it takes the
6951                // same candidate gate and reparse (#6902). Only this node's own
6952                // recovery ends its visit: a recovery walk can reach members of
6953                // a region another recovery consumed, and those stay walked.
6954                if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
6955                    self.visit_declaration(node, scope, true, stack, ancestry)
6956                }
6957            }
6958            "preproc_call" => self.visit_preproc_call(node),
6959            "type_definition" | "alias_declaration" => {
6960                self.visit_type_declaration(node, scope, stack, ancestry)
6961            }
6962            "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6963            // `#include` is collected by the coordinated primary events before the
6964            // walk, so a directive the container walk never reaches -- inside
6965            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
6966            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
6967            // include claim.
6968            "preproc_include" => {}
6969            // The grammar represents a split extern-C condition as sibling
6970            // markers; declarations between them remain unconditional file
6971            // scope items and need no special declaration metadata.
6972            "preproc_linkage_open" | "preproc_linkage_close" => {}
6973            kind if preserves_declaration_scope_through_wrapper(
6974                kind,
6975                scope.class_unit.is_some(),
6976            ) =>
6977            {
6978                // A preprocessor conditional gates every declaration inside it
6979                // on a configuration this analyzer never evaluates; record the
6980                // interval so declaration state can say so (issue #1476). The
6981                // else/elif branches are children of the `preproc_if` node, so
6982                // recording the openers covers every branch.
6983                if kind == "labeled_statement" {
6984                    self.visit_access_label_constructor(node, scope, ancestry);
6985                }
6986                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6987                    let mut range = cpp_declaration_range(node);
6988                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6989                        range.end_byte = boundary.end_byte;
6990                        range.end_line = boundary.end_line;
6991                    }
6992                    self.parsed.record_materialization(
6993                        MaterializationRecord::ConfigurationConditional { range },
6994                    );
6995                    if node.has_error() {
6996                        // A malformed export-macro class can close the namespace
6997                        // node early while the enclosing include guard still owns
6998                        // the remaining class-head/body pairs. The ordinary walk
6999                        // cannot carry the lost namespace through those promoted
7000                        // siblings. Scan only structured ERROR nodes in this
7001                        // already-malformed conditional; the pair recovery's
7002                        // exact class/macro/body predicate remains the admission
7003                        // gate, and its namespace lifting restores the owner.
7004                        let mut candidates = vec![node];
7005                        while let Some(candidate) = candidates.pop() {
7006                            // An `ERROR` still inside a namespace body is one
7007                            // the ordinary walk reaches with that namespace in
7008                            // scope. Claiming it here registers the recovered
7009                            // class at file scope and the consumed region then
7010                            // suppresses the walk that would have named it
7011                            // correctly -- which is why Botan's `DL_Group` was
7012                            // `DL_Group` and not `Botan.DL_Group` in the real
7013                            // header, where an include guard wraps the
7014                            // namespace, and was right in a fixture without one
7015                            // (#2552).
7016                            if candidate.kind() == "ERROR"
7017                                && !cpp_is_inside_namespace_body(candidate, ancestry)
7018                                && self.visit_function_like_export_class_pair(
7019                                    candidate, scope, stack, ancestry,
7020                                )
7021                            {
7022                                continue;
7023                            }
7024                            for index in (0..candidate.named_child_count()).rev() {
7025                                candidates.push(
7026                                    candidate
7027                                        .named_child(index)
7028                                        .expect("index below the node's own named child count"),
7029                                );
7030                            }
7031                        }
7032                    }
7033                }
7034                stack.push(CppWork::Container(CppContainer {
7035                    node,
7036                    scope: scope.clone(),
7037                }))
7038            }
7039            // A macro invocation the parser could not group leaves its head in
7040            // whatever slot was free. The `function_definition` and `ERROR`
7041            // arms above try the same recovery on the envelopes they get; a
7042            // flattened run leaves a bare `identifier`/`type_identifier`, and a
7043            // swallowed tail can land in a `parameter_declaration` (#3094).
7044            _ => {
7045                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
7046            }
7047        }
7048    }
7049
7050    fn visit_macro_swallowed_function_declarations<'tree>(
7051        &mut self,
7052        envelope: Node<'tree>,
7053        scope: &ScopeInfo,
7054    ) {
7055        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7056            || envelope.kind() == "ERROR"
7057                && envelope
7058                    .parent()
7059                    .is_some_and(|parent| parent.kind() == "ERROR")
7060        {
7061            return;
7062        }
7063        let mut stack = (0..envelope.named_child_count())
7064            .filter_map(|index| envelope.named_child(index))
7065            .collect::<Vec<_>>();
7066        while let Some(node) = stack.pop() {
7067            if node.kind() == "function_declarator" {
7068                self.visit_error_swallowed_function_declaration(node, scope);
7069            }
7070            for child in named_children_iter(node) {
7071                stack.push(child);
7072            }
7073        }
7074    }
7075
7076    /// Index the declarations an attribute-like macro invocation swallowed into
7077    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
7078    /// the shape and why the parser produces it.
7079    fn visit_macro_wrapped_declarations<'tree>(
7080        &mut self,
7081        envelope: Node<'tree>,
7082        scope: &ScopeInfo,
7083        ancestry: &ParentIndex<'tree>,
7084    ) {
7085        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7086        if recovered.is_empty() {
7087            return;
7088        }
7089        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7090        self.record_recovered_declarations(recovery, envelope, |visitor| {
7091            for declaration in recovered {
7092                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7093            }
7094        });
7095    }
7096
7097    /// Index the declarations a macro invocation collapsed. See
7098    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
7099    /// produces it. Returns whether it claimed the region beginning at
7100    /// `envelope`.
7101    ///
7102    /// The parser's own nodes cannot be read the way the swallowed tail of the
7103    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
7104    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
7105    /// with no declarator left at all, and the declarators that do survive on
7106    /// the envelope's declarator spine pair one declaration's name with the
7107    /// *next* declaration's parameter list. Reading those would be a guess.
7108    ///
7109    /// The bytes are still ordinary declarations, though, and the collapse is
7110    /// the parser carrying the failure forward from one macro invocation. So
7111    /// reparse the region, walk the items the parser makes of it, and when one
7112    /// item is itself a collapsed run, recover that invocation from its own
7113    /// bytes and resume the scan just past it. Each pass starts later than the
7114    /// last, so the scan is a loop over the invocations that collapse, not over
7115    /// the declarations: `llama.h` needs three passes for 214 declarations.
7116    ///
7117    /// The region can reach past `envelope` into the siblings the parser handed
7118    /// the rest of one invocation, so it is recorded as consumed: those
7119    /// siblings are this recovery's, and the ordinary walk must not read them
7120    /// again. Recording it after the scan, not before, keeps the scan's own
7121    /// reparsed nodes -- which carry their original byte offsets -- visible to
7122    /// the walk it drives.
7123    fn visit_collapsed_macro_declaration_run<'tree>(
7124        &mut self,
7125        envelope: Node<'tree>,
7126        scope: &ScopeInfo,
7127        ancestry: &ParentIndex<'tree>,
7128    ) -> bool {
7129        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7130            return false;
7131        };
7132        let start = envelope.start_byte();
7133        let end = run.region_end;
7134        let recovery = cpp_recovery_window(self.source, start, end);
7135        self.record_recovered_declarations(recovery, envelope, |visitor| {
7136            let mut position = start;
7137            while position < end {
7138                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7139                    return;
7140                };
7141                let root = tree.root_node();
7142                // A region reparse is its own tree and needs its own parent
7143                // index and dependency arena; the caller's answers nothing
7144                // about these nodes.
7145                let ancestry = visitor.recovery_ancestry(root);
7146                let mut cursor = root.walk();
7147                let collapsed =
7148                    root.named_children(&mut cursor)
7149                        .enumerate()
7150                        .find_map(|(index, item)| {
7151                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7152                                .map(|run| (index, item, run))
7153                        });
7154                // Walk only the items before the collapsed one. It swallowed
7155                // the rest of the region, so handing it to the ordinary walk
7156                // would re-enter this recovery on a region that starts where
7157                // this one did.
7158                let mut stack = Vec::new();
7159                push_cpp_sibling_range(
7160                    root,
7161                    0,
7162                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7163                    scope.clone(),
7164                    &mut stack,
7165                );
7166                visitor.drain_cpp_work(stack, &ancestry);
7167                let Some((_, item, run)) = collapsed else {
7168                    return;
7169                };
7170                // On its own bytes the invocation is the declaration-scope
7171                // shape `macro_wrapped_declarations` reads, so its wrapped
7172                // declaration needs no reader of its own here.
7173                if let Some(head) =
7174                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7175                {
7176                    let head_root = head.root_node();
7177                    let head_ancestry = visitor.recovery_ancestry(head_root);
7178                    visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7179                }
7180                assert!(
7181                    run.invocation_end > position,
7182                    "a collapsed run at {position} must end after the byte the scan resumed \
7183                     from, but ended at {}",
7184                    run.invocation_end
7185                );
7186                position = run.invocation_end;
7187            }
7188        });
7189        self.consumed_fragment_regions.push((start, end));
7190        true
7191    }
7192
7193    /// Index the members written under an access label that a class-scope
7194    /// macro invocation swallowed. See [`cpp_macro_swallowed_access_label`]
7195    /// for the shape.
7196    ///
7197    /// The members are whole in the source but not in this tree, and at file
7198    /// scope a bodiless member such as `Ctor(int a);` reads as a variable
7199    /// declaration, so the reparse keeps the class around them: the class head
7200    /// through its `{`, the label and its members, and the class's `}`, as one
7201    /// included-range parse at the original offsets. That is the parse the
7202    /// class gets with the invocation's bytes left out, which is what the
7203    /// replacement's own declarations would have to be read without anyway.
7204    /// The reparsed label is a real `access_specifier`, so the members carry the
7205    /// access they are written under. Only the members the label swallowed are
7206    /// walked; the class's other members are the ordinary walk's. When the
7207    /// reparse does not read the label back, nothing is published: the
7208    /// field this node would otherwise mint is named after the access keyword.
7209    fn visit_macro_swallowed_access_label_members<'tree>(
7210        &mut self,
7211        node: Node<'tree>,
7212        label: &CppSwallowedAccessLabel<'tree>,
7213        scope: &ScopeInfo,
7214        ancestry: &ParentIndex<'tree>,
7215    ) {
7216        let Some(body) = ancestry.parent(node) else {
7217            return;
7218        };
7219        let Some(class_like) = ancestry.parent(body) else {
7220            return;
7221        };
7222        // The reparse holds the class itself, so the recovery's source context
7223        // is the class's enclosing one; the class's own scope would name it
7224        // twice in the recovered members' lexical paths.
7225        let Some(class_parent) = ancestry.parent(class_like) else {
7226            return;
7227        };
7228        let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7229            return;
7230        };
7231        if body.kind() != "field_declaration_list"
7232            || open.kind() != "{"
7233            || close.kind() != "}"
7234            || close.is_missing()
7235        {
7236            return;
7237        }
7238        let start = label.keyword.start_byte();
7239        let end = node.end_byte();
7240        let Some(tree) = parse_source_ranges_with_cancellation(
7241            &tree_sitter_cpp::LANGUAGE.into(),
7242            self.source,
7243            &[
7244                (class_like.start_byte(), open.end_byte()),
7245                (start, end),
7246                (close.start_byte(), close.end_byte()),
7247            ],
7248            None,
7249        ) else {
7250            return;
7251        };
7252        let root = tree.root_node();
7253        let Some(reparsed_body) = root
7254            .descendant_for_byte_range(open.start_byte(), open.end_byte())
7255            .and_then(|brace| brace.parent())
7256            .filter(|parent| parent.kind() == "field_declaration_list")
7257        else {
7258            return;
7259        };
7260        let mut cursor = reparsed_body.walk();
7261        let members = reparsed_body
7262            .named_children(&mut cursor)
7263            .enumerate()
7264            .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7265            .map(|(index, member)| (index, member.kind(), member.start_byte()))
7266            .collect::<Vec<_>>();
7267        let Some(&(first, "access_specifier", label_start)) = members.first() else {
7268            return;
7269        };
7270        if label_start != start {
7271            return;
7272        }
7273        let last = members.last().expect("the label is a member").0;
7274        let reparsed_ancestry = self.recovery_ancestry(root);
7275        let recovery = cpp_recovery_window(self.source, start, end);
7276        self.record_recovered_declarations(recovery, class_parent, |visitor| {
7277            let mut stack = Vec::new();
7278            push_cpp_sibling_range(
7279                reparsed_body,
7280                first + 1,
7281                last + 1,
7282                scope.clone(),
7283                &mut stack,
7284            );
7285            visitor.drain_cpp_work(stack, &reparsed_ancestry);
7286        });
7287    }
7288
7289    /// Index the members a string-argument attribute macro stranded in an
7290    /// `ERROR` inside a class body.
7291    ///
7292    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
7293    /// grouping of the attributed member and of every member written after it
7294    /// until it recovers. The members are still whole `function_declarator`
7295    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
7296    /// export-macro class such as Botan's `DL_Group` was indexed with no
7297    /// members at all (#2552).
7298    fn visit_stranded_class_members<'tree>(
7299        &mut self,
7300        node: Node<'tree>,
7301        scope: &ScopeInfo,
7302        ancestry: &ParentIndex<'tree>,
7303    ) {
7304        if scope.class_unit.is_none() || !scope.declarations_are_fields {
7305            return;
7306        }
7307        for member in stranded_declaration_run(node, self.source).declarations {
7308            self.add_macro_wrapped_declaration(member, scope, ancestry);
7309        }
7310    }
7311
7312    /// Index the constructor an access label swallowed in a reparsed class
7313    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
7314    ///
7315    /// The declarator is recovered by reparsing from that call to the end of
7316    /// the label's own statement, which is the same offset-preserving region
7317    /// reparse every other recovery here uses, so the recovered nodes carry
7318    /// their true source positions.
7319    fn visit_access_label_constructor<'tree>(
7320        &mut self,
7321        node: Node<'tree>,
7322        scope: &ScopeInfo,
7323        ancestry: &ParentIndex<'tree>,
7324    ) {
7325        let Some(class_unit) = scope.class_unit.clone() else {
7326            return;
7327        };
7328        if !scope.declarations_are_fields {
7329            return;
7330        }
7331        let class_name = class_unit.identifier().to_string();
7332        // Statement context reads pointer parameters as multiplication. Use the
7333        // same class-context validation as an unlabelled constructor statement.
7334        let statement = node.named_children(&mut node.walk()).find(|child| {
7335            cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7336        });
7337        if let Some(statement) = statement {
7338            self.visit_bare_constructor_expression(statement, scope, ancestry);
7339            return;
7340        }
7341        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7342        else {
7343            return;
7344        };
7345        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7346            return;
7347        };
7348        let root = tree.root_node();
7349        let Some(declarator) =
7350            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7351        else {
7352            return;
7353        };
7354        let reparsed_ancestry = self.recovery_ancestry(root);
7355        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7356        let range = cpp_declaration_range(definition.unwrap_or(declarator));
7357        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7358        self.record_recovered_declarations(recovery, node, |visitor| {
7359            visitor.add_macro_wrapped_declaration(
7360                MacroWrappedDeclaration {
7361                    declarator,
7362                    range,
7363                    is_static: false,
7364                },
7365                scope,
7366                &reparsed_ancestry,
7367            );
7368        });
7369        // This constructor has been accounted for. Its statement-context
7370        // initializer declarators do not declare additional members.
7371        self.consumed_fragment_regions.push((start, range.end_byte));
7372    }
7373
7374    /// Index the members a string-argument attribute macro stranded inside the
7375    /// `macro_type_specifier` the macro-declaration grammar reads them as (#2552
7376    /// shape 3 under the new grammar).
7377    ///
7378    /// `BOTAN_DEPRECATED("text") explicit Ctor(T); Ctor(U, V);` keeps the macro as
7379    /// a `macro_type_specifier` whose argument region is an `ERROR` holding the
7380    /// string, the attributed member as a `type_descriptor`, and its `;`, while
7381    /// the member after it becomes the specifier's own `type` field. Neither
7382    /// member has a real declarator node -- an abstract function declarator in a
7383    /// `type_descriptor` carries only parameters -- but each member's source range
7384    /// is whole, so reparse it and read the declarator from the reparse, which is
7385    /// the same offset-preserving region reparse every other recovery here uses.
7386    /// The admission gate is the same one every reparse shares: exactly one clean
7387    /// callable declaration spanning the member's range.
7388    fn visit_string_attribute_macro_type_members<'tree>(
7389        &mut self,
7390        node: Node<'tree>,
7391        scope: &ScopeInfo,
7392    ) -> bool {
7393        if node.kind() != "field_declaration" {
7394            return false;
7395        }
7396        let Some(specifier) = node
7397            .child_by_field_name("type")
7398            .filter(|type_node| type_node.kind() == "macro_type_specifier")
7399        else {
7400            return false;
7401        };
7402        if specifier
7403            .child_by_field_name("name")
7404            .is_none_or(|name| name.kind() != "identifier")
7405        {
7406            return false;
7407        }
7408        // The attributed member sits inside the argument `ERROR`: the string's
7409        // quote is the first child, the member's `;` the last, and its
7410        // type-descriptor is in between.
7411        let mut cursor = specifier.walk();
7412        let Some(argument_error) = specifier
7413            .named_children(&mut cursor)
7414            .find(|child| child.kind() == "ERROR")
7415        else {
7416            return false;
7417        };
7418        if argument_error
7419            .child(0)
7420            .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7421            || argument_error
7422                .child(argument_error.child_count().saturating_sub(1))
7423                .is_none_or(|last| last.kind() != ";" || last.is_missing())
7424        {
7425            return false;
7426        }
7427        let mut members: Vec<((usize, usize), String)> = Vec::new();
7428        for index in 0..argument_error.child_count() {
7429            let Some(child) = argument_error.child(index) else {
7430                continue;
7431            };
7432            if child.kind() == "type_descriptor" {
7433                let start = (0..index)
7434                    .rev()
7435                    .find_map(|previous| {
7436                        argument_error
7437                            .child(previous)
7438                            .filter(|sibling| sibling.kind() == "explicit")
7439                    })
7440                    .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7441                let Some(name) = child
7442                    .child_by_field_name("type")
7443                    .and_then(|type_node| direct_identifier_name(type_node, self.source))
7444                else {
7445                    return false;
7446                };
7447                members.push(((start, argument_error.end_byte()), name));
7448            }
7449        }
7450        let [(first_range, first_name)] = members.as_slice() else {
7451            return false;
7452        };
7453        let Some(second) = specifier
7454            .child_by_field_name("type")
7455            .filter(|type_node| type_node.kind() == "type_descriptor")
7456        else {
7457            return false;
7458        };
7459        let Some(second_name) = second
7460            .child_by_field_name("type")
7461            .and_then(|type_node| direct_identifier_name(type_node, self.source))
7462        else {
7463            return false;
7464        };
7465        let members = [
7466            (*first_range, first_name.as_str()),
7467            ((second.start_byte(), node.end_byte()), second_name.as_str()),
7468        ];
7469        let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7470        self.record_recovered_declarations(recovery, node, |visitor| {
7471            for ((start, end), member_name) in members {
7472                let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7473                    continue;
7474                };
7475                let root = tree.root_node();
7476                // The file-scope reparse of a member-shaped head is errorful
7477                // (a member qualifier is not valid there), so the admission is
7478                // the declarator itself: exactly one function declarator
7479                // naming the member inside its own range.
7480                let mut candidate = None;
7481                let mut stack = vec![root];
7482                while let Some(current) = stack.pop() {
7483                    if current.kind() == "function_declarator"
7484                        && current.start_byte() >= start
7485                        && current.end_byte() <= end
7486                        && cpp_function_declarator_name_node(current)
7487                            .is_some_and(|name| node_text(name, visitor.source) == member_name)
7488                    {
7489                        if candidate.is_some() {
7490                            candidate = None;
7491                            break;
7492                        }
7493                        candidate = Some(current);
7494                        continue;
7495                    }
7496                    let mut cursor = current.walk();
7497                    stack.extend(current.named_children(&mut cursor));
7498                }
7499                let Some(declarator) = candidate else {
7500                    continue;
7501                };
7502                let reparsed_ancestry = visitor.recovery_ancestry(root);
7503                visitor.add_macro_wrapped_declaration(
7504                    MacroWrappedDeclaration {
7505                        declarator,
7506                        range: cpp_recovery_window(visitor.source, start, end),
7507                        is_static: false,
7508                    },
7509                    scope,
7510                    &reparsed_ancestry,
7511                );
7512            }
7513        });
7514        true
7515    }
7516
7517    fn add_macro_wrapped_declaration<'tree>(
7518        &mut self,
7519        declaration: MacroWrappedDeclaration<'tree>,
7520        scope: &ScopeInfo,
7521        ancestry: &ParentIndex<'tree>,
7522    ) {
7523        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7524        else {
7525            return;
7526        };
7527        let code_unit =
7528            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7529        let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7530        let is_constructor = scope
7531            .class_unit
7532            .as_ref()
7533            .is_some_and(|class| function.name == class.identifier())
7534            || cpp_callable_is_structural_constructor(
7535                declaration.declarator,
7536                self.source,
7537                ancestry,
7538            );
7539        let already_declared = self.parsed.contains_declaration(&code_unit);
7540        if already_declared {
7541            self.parsed
7542                .record_navigation_range(code_unit.clone(), declaration.range);
7543            if definition.is_none() {
7544                return;
7545            }
7546            // Preserve the earlier declaration-only occurrence while adding
7547            // the recovered body as a distinct definition occurrence.
7548        }
7549        self.add_declaration_with_range(
7550            ancestry,
7551            code_unit.clone(),
7552            declaration.range,
7553            Some(declaration.declarator),
7554            None,
7555            None,
7556        );
7557        if definition.is_some() {
7558            let recovered_declaration = *self
7559                .source_links
7560                .get(&code_unit)
7561                .and_then(|links| links.last())
7562                .expect("recovered definition source");
7563            let mut properties = self.source_properties.borrow_mut();
7564            let fact = properties
7565                .get_mut(&recovered_declaration)
7566                .expect("recovered definition source fact");
7567            fact.occurrence_role =
7568                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7569            fact.callable_is_constructor |= is_constructor;
7570        }
7571        let signature = normalize_cpp_whitespace(
7572            self.source
7573                .get(declaration.range.start_byte..declaration.range.end_byte)
7574                .expect("a recovered declaration range covers one source range"),
7575        );
7576        let linkage = if declaration.is_static {
7577            CallableLinkage::Internal
7578        } else {
7579            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7580        };
7581        // Whether this is a definition is a property of the recovered node, not
7582        // of the caller: a declarator that a `function_definition` gives a body
7583        // is a definition wherever the recovery found it.
7584        let declaration_only = definition.is_none();
7585        self.add_signature_with_metadata(
7586            code_unit.clone(),
7587            cpp_signature_metadata(
7588                signature,
7589                declaration.declarator,
7590                linkage,
7591                self.source,
7592                ancestry,
7593            )
7594            .with_declaration_only(declaration_only),
7595        );
7596        if already_declared {
7597            return;
7598        }
7599        if let Some(parent) = &scope.class_unit {
7600            self.parsed.add_child(parent.clone(), code_unit);
7601        } else if let Some(module) = &scope.module {
7602            self.parsed.add_child(module.clone(), code_unit);
7603        }
7604    }
7605
7606    /// Index the local types of an ordinary function body. Function bodies are
7607    /// otherwise outside the declaration walk, but their anonymous aggregate
7608    /// owners must exist so structured local binding inference can name their
7609    /// fields (#2994), and a named C++ local class and its members are query
7610    /// targets of their own (#6902).
7611    fn visit_local_types_in_function<'tree>(
7612        &mut self,
7613        function: Node<'tree>,
7614        callable: &CodeUnit,
7615        scope: &ScopeInfo,
7616        stack: &mut Vec<CppWork<'tree>>,
7617        ancestry: &ParentIndex<'tree>,
7618    ) {
7619        let Some(body) = cpp_body_node(function) else {
7620            return;
7621        };
7622        // A body opens a block scope, so nothing declared in it is a member of
7623        // the class the definition may be written inside. Dropping the
7624        // aggregate carrier gives an inline member function's body the same
7625        // block scope an out-of-line `void C::f()` body already has; without
7626        // it a C++ header, where member bodies are usually inline, would keep
7627        // none of these owners (#3301).
7628        let block_scope = ScopeInfo {
7629            class_unit: None,
7630            declarations_are_fields: false,
7631            local_block: Some(CppLocalBlock::new(callable, body)),
7632            ..scope.clone()
7633        };
7634        let scope = &block_scope;
7635        let mut pending = vec![body];
7636        while let Some(node) = pending.pop() {
7637            if matches!(node.kind(), "function_definition" | "lambda_expression") {
7638                continue;
7639            }
7640            if node.kind() == "declaration"
7641                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7642            {
7643                continue;
7644            }
7645            if matches!(
7646                node.kind(),
7647                "class_specifier" | "struct_specifier" | "union_specifier"
7648            ) {
7649                self.visit_function_local_class(node, callable, scope, stack, ancestry);
7650                continue;
7651            }
7652            let mut cursor = node.walk();
7653            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7654            children.reverse();
7655            pending.extend(children);
7656        }
7657    }
7658
7659    /// Mint a named C++ class defined in a function body under the identity
7660    /// of the block it is written in (see [`CppLocalBlock`]). Its members are
7661    /// owned by the class through the ordinary class-body walk.
7662    ///
7663    /// C block-scope tags are a separate capability and keep their current
7664    /// absence. A header is read both ways, so a minted local class is a
7665    /// declaration whose existence differs between the two readings, which is
7666    /// what the C-reading witness records.
7667    fn visit_function_local_class<'tree>(
7668        &mut self,
7669        node: Node<'tree>,
7670        callable: &CodeUnit,
7671        scope: &ScopeInfo,
7672        stack: &mut Vec<CppWork<'tree>>,
7673        ancestry: &ParentIndex<'tree>,
7674    ) {
7675        if self.c_tag_semantics || cpp_body_node(node).is_none() {
7676            return;
7677        }
7678        let Some(name) = class_like_name(node, self.source, ancestry) else {
7679            return;
7680        };
7681        let block =
7682            std::iter::successors(ancestry.parent(node), |current| ancestry.parent(*current))
7683                .find(|ancestor| ancestor.kind() == "compound_statement")
7684                .expect("a function-local class is written inside the function body");
7685        self.c_tag_scope_witness = true;
7686        let local_scope = ScopeInfo {
7687            package_name: callable.package_name().to_string(),
7688            class_unit: None,
7689            template_signature: None,
7690            template_metadata: None,
7691            declarations_are_fields: false,
7692            recovered_specialization_member_scope: false,
7693            local_block: Some(CppLocalBlock::new(callable, block)),
7694            ..scope.clone()
7695        };
7696        self.visit_named_class_like(node, name, &local_scope, stack, ancestry);
7697    }
7698
7699    fn visit_error_swallowed_function_declaration<'tree>(
7700        &mut self,
7701        node: Node<'tree>,
7702        scope: &ScopeInfo,
7703    ) {
7704        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7705            return;
7706        };
7707        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7708            return;
7709        };
7710        let root = tree.root_node();
7711        let mut cursor = root.walk();
7712        let declarations = root
7713            .named_children(&mut cursor)
7714            .filter(|child| child.kind() != "comment")
7715            .collect::<Vec<_>>();
7716        let [declaration] = declarations.as_slice() else {
7717            return;
7718        };
7719        if declaration.kind() != "declaration"
7720            || declaration.has_error()
7721            || declaration.start_byte() != start
7722            || declaration.end_byte() != end
7723        {
7724            return;
7725        }
7726        let recovery = cpp_recovery_window(self.source, start, end);
7727        // The reparsed region is its own tree, so this walk indexes it itself.
7728        let reparsed_ancestry = self.recovery_ancestry(root);
7729        self.record_recovered_declarations(recovery, node, |visitor| {
7730            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7731        });
7732    }
7733
7734    /// Recover a K&R-compatibility prototype macro invocation such as
7735    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
7736    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
7737    /// defined only in headers outside this workspace (ruby's own
7738    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
7739    /// `name _((args))` and turns each such line into a malformed
7740    /// `declaration` or pointer-expression statement. Before this
7741    /// recovery, the ordinary declaration visitor read that wreckage as a
7742    /// field with the type and name swapped (e.g. a field literally named
7743    /// `VALUE`) and indexed no prototype for the real name at all.
7744    ///
7745    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
7746    /// preserve the declared name, a known one-argument compatibility macro,
7747    /// the nested argument list, and a live terminator. It rejects arbitrary
7748    /// identifiers and bare `ERROR` envelopes that flattened those relations;
7749    /// guessing there previously turned malformed non-macro declarations into
7750    /// invented functions. For each proven candidate this reparses the range
7751    /// before the macro token, the inner parenthesized argument node, and the
7752    /// terminating `;` as one included-range parse. That is the parser-native
7753    /// equivalent of expanding `#define _(args) args` while retaining original
7754    /// byte offsets. The result is admitted only when it is exactly one clean
7755    /// function prototype spanning the recovered run.
7756    ///
7757    /// A class-body line is a `field_declaration`, and a bodiless member read
7758    /// at file scope loses the class-body position its `static` and access
7759    /// facts depend on. That reparse therefore keeps the class around the
7760    /// prototype, as [`PrototypeMacroClassEnvelope`] describes, and admits
7761    /// exactly one clean member prototype in the reparsed body instead.
7762    ///
7763    /// Returns whether a candidate was recovered.
7764    fn visit_prototype_macro_declarations<'tree>(
7765        &mut self,
7766        node: Node<'tree>,
7767        scope: &ScopeInfo,
7768        ancestry: &ParentIndex<'tree>,
7769    ) -> bool {
7770        let candidates = cpp_prototype_macro_candidates(node, self.source);
7771        if candidates.is_empty() {
7772            return false;
7773        }
7774        let envelope = if node.kind() == "field_declaration" {
7775            let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7776            else {
7777                return false;
7778            };
7779            Some(envelope)
7780        } else {
7781            None
7782        };
7783        let mut recovered = false;
7784        for candidate in candidates {
7785            let start = candidate.run_start;
7786            let end = candidate.semicolon_end;
7787            if self.byte_range_is_inside_consumed_fragment(start, end) {
7788                continue;
7789            }
7790            let mut ranges = Vec::with_capacity(6);
7791            if let Some(envelope) = &envelope {
7792                ranges.push(envelope.head);
7793                ranges.extend(envelope.label);
7794            }
7795            ranges.extend(candidate.ranges());
7796            if let Some(envelope) = &envelope {
7797                ranges.push(envelope.close);
7798            }
7799            let Some(tree) = parse_source_ranges_with_cancellation(
7800                &tree_sitter_cpp::LANGUAGE.into(),
7801                self.source,
7802                &ranges,
7803                None,
7804            ) else {
7805                continue;
7806            };
7807            let root = tree.root_node();
7808            let (container, expected_kind) = match &envelope {
7809                None => (root, "declaration"),
7810                Some(envelope) => {
7811                    let Some(body) = root
7812                        .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7813                        .and_then(|brace| brace.parent())
7814                        .filter(|parent| parent.kind() == "field_declaration_list")
7815                    else {
7816                        continue;
7817                    };
7818                    (body, "field_declaration")
7819                }
7820            };
7821            // Everything the envelope contributes ends before the prototype.
7822            let mut cursor = container.walk();
7823            let declarations = container
7824                .named_children(&mut cursor)
7825                .enumerate()
7826                .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7827                .collect::<Vec<_>>();
7828            let [(index, declaration)] = declarations.as_slice() else {
7829                continue;
7830            };
7831            if declaration.kind() != expected_kind
7832                || declaration.has_error()
7833                || declaration.start_byte() != start
7834                || declaration.end_byte() != end
7835                || declaration
7836                    .child_by_field_name("declarator")
7837                    .and_then(extract_function_declarator)
7838                    .is_none()
7839            {
7840                continue;
7841            }
7842            let index = *index;
7843            let recovery = cpp_recovery_window(self.source, start, end);
7844            // The reparsed region is its own tree, so this walk indexes it
7845            // itself. A class envelope holds the class, so the recovery's
7846            // source context is the class's enclosing one; the class's own
7847            // would name it twice in the member's lexical path.
7848            let reparsed_ancestry = self.recovery_ancestry(root);
7849            let origin = envelope
7850                .as_ref()
7851                .map_or(node, |envelope| envelope.class_parent);
7852            self.record_recovered_declarations(recovery, origin, |visitor| {
7853                let mut stack = Vec::new();
7854                push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7855                visitor.drain_cpp_work(stack, &reparsed_ancestry);
7856            });
7857            self.consumed_fragment_regions.push((start, end));
7858            recovered = true;
7859        }
7860        recovered
7861    }
7862
7863    /// Declare one Module per namespace level of `levels` under `package_name`,
7864    /// each recording the range of the construct that writes it, and return the
7865    /// innermost level's package name and Module. A level an earlier definition
7866    /// already declared is reused, which is also how a reopened namespace keeps
7867    /// the range of the definition that first declared it.
7868    fn declare_namespace_levels(
7869        &mut self,
7870        mut package_name: String,
7871        levels: Vec<(String, Range, Option<Range>)>,
7872        node: Node<'_>,
7873        ancestry: &ParentIndex<'_>,
7874    ) -> (String, Option<CodeUnit>) {
7875        let mut module = None;
7876        for (component, range, name_range) in levels {
7877            let full_name = if package_name.is_empty() {
7878                component
7879            } else {
7880                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7881            };
7882            let level = CodeUnit::new_fq(
7883                self.file.clone(),
7884                CodeUnitType::Module,
7885                "",
7886                full_name.clone(),
7887                cpp_namespace_fq(&full_name),
7888            );
7889            if !self.parsed.contains_declaration(&level) {
7890                if let Some(name_range) = name_range {
7891                    self.add_declaration_with_name_range(
7892                        level.clone(),
7893                        range,
7894                        name_range,
7895                        Some((ancestry, node)),
7896                        None,
7897                        None,
7898                    );
7899                } else {
7900                    self.add_declaration_with_range(
7901                        ancestry,
7902                        level.clone(),
7903                        range,
7904                        Some(node),
7905                        None,
7906                        None,
7907                    );
7908                }
7909            }
7910            package_name = full_name;
7911            module = Some(level);
7912        }
7913        (package_name, module)
7914    }
7915
7916    /// [`Self::declare_namespace_levels`] for levels a recovered-namespace
7917    /// index restores. Each level carries the range of its own name token,
7918    /// which the walk's current node cannot supply: that node is whichever
7919    /// declaration the walk reached first inside the region, and deriving a
7920    /// level's name from it paired the level's range with an unrelated
7921    /// construct's name token (#4292).
7922    fn declare_recovered_namespace_levels(
7923        &mut self,
7924        mut package_name: String,
7925        levels: Vec<(String, Range, Range)>,
7926    ) -> (String, Option<CodeUnit>) {
7927        let mut module = None;
7928        for (component, range, name_range) in levels {
7929            let full_name = if package_name.is_empty() {
7930                component
7931            } else {
7932                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7933            };
7934            let level = CodeUnit::new_fq(
7935                self.file.clone(),
7936                CodeUnitType::Module,
7937                "",
7938                full_name.clone(),
7939                cpp_namespace_fq(&full_name),
7940            );
7941            if !self.parsed.contains_declaration(&level) {
7942                self.add_declaration_with_name_range(
7943                    level.clone(),
7944                    range,
7945                    name_range,
7946                    None,
7947                    None,
7948                    None,
7949                );
7950            }
7951            package_name = full_name;
7952            module = Some(level);
7953        }
7954        (package_name, module)
7955    }
7956
7957    /// Restore the complete lexical namespace path when recovery dropped an
7958    /// ancestor or retained it past its real close. Parsed namespaces beginning
7959    /// inside the recovered region still contribute their normal nesting.
7960    /// Crossing that boundary also clears any malformed class owner.
7961    fn recovered_namespace_scope(
7962        &mut self,
7963        node: Node<'_>,
7964        scope: &ScopeInfo,
7965    ) -> Option<ScopeInfo> {
7966        self.orphaned_namespaces.region_at(node.start_byte())?;
7967        // Each level carries the `namespace Name { ... }` construct that writes
7968        // it along with the level's own name token. Declaring them at `node`
7969        // instead recorded the range of whichever member the walk happened to
7970        // reach first, so a namespace whose head collapsed into an ERROR
7971        // reported its first inner declaration's lines as its own (#3309), and
7972        // deriving the name from that node paired the level's range with the
7973        // member's name token (#4292).
7974        let levels = self
7975            .orphaned_namespaces
7976            .enclosing_namespace_levels(node, self.source);
7977        let package_name = levels
7978            .iter()
7979            .map(|(component, ..)| component.as_str())
7980            .collect::<Vec<_>>()
7981            .join(CPP_PACKAGE_SEPARATOR);
7982        if package_name == scope.package_name {
7983            return None;
7984        }
7985        let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7986        Some(ScopeInfo {
7987            package_name,
7988            module,
7989            // A recovered namespace body is no longer inside the class (or
7990            // function) that tree-sitter accidentally used as its wrapper.
7991            // Keep namespace/module context, but never carry that owner into
7992            // declarations published from the recovered region.
7993            class_unit: None,
7994            template_signature: None,
7995            template_metadata: None,
7996            declarations_are_fields: false,
7997            recovered_specialization_member_scope: false,
7998            visible_using_namespaces: scope.visible_using_namespaces.clone(),
7999            local_block: None,
8000            visible_using_declarations: scope.visible_using_declarations.clone(),
8001        })
8002    }
8003
8004    fn visit_namespace<'tree>(
8005        &mut self,
8006        node: Node<'tree>,
8007        scope: &ScopeInfo,
8008        stack: &mut Vec<CppWork<'tree>>,
8009        ancestry: &ParentIndex<'tree>,
8010    ) {
8011        let name_node = node.child_by_field_name("name");
8012        let Some(name_node) = name_node else {
8013            if let Some(body) = cpp_body_node(node) {
8014                stack.push(CppWork::Container(CppContainer {
8015                    node: body,
8016                    scope: scope.clone(),
8017                }));
8018            }
8019            return;
8020        };
8021        // Diagnostic corpora contain deliberately ill-formed global namespace
8022        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
8023        // the leading global `::` as the first anonymous child. Honor that AST
8024        // boundary instead of appending the name to the lexical namespace;
8025        // appending produced legacy names such as `outer::::outer::inner`, which
8026        // could not round-trip through the structured FqName boundary.
8027        let explicitly_global = name_node
8028            .child(0)
8029            .is_some_and(|child| !child.is_named() && child.kind() == "::");
8030        let components = cpp_namespace_name_components(name_node, self.source);
8031        if components.is_empty() {
8032            return;
8033        }
8034        // One Module per namespace level. C++17's `namespace a::b { ... }` is
8035        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
8036        // shorthand must declare `a` as well as `a::b` -- extracting only the
8037        // innermost level left the enclosing namespace undeclared and made the
8038        // two spellings of one construct disagree (issue #1878).
8039        let package_name = if explicitly_global {
8040            String::new()
8041        } else {
8042            scope.package_name.clone()
8043        };
8044        let definition = node_range(node);
8045        // The `namespace a::b` shorthand names every level in one node: each
8046        // level's name occurrence is its own token, the same evidence the
8047        // recovered-namespace path carries per level (#4292).
8048        let mut component_ranges =
8049            cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
8050        let (package_name, module) = self.declare_namespace_levels(
8051            package_name,
8052            components
8053                .into_iter()
8054                .map(|component| {
8055                    let name_range = component_ranges
8056                        .next()
8057                        .filter(|(name, _)| *name == component)
8058                        .map(|(_, range)| range);
8059                    (component, definition, name_range)
8060                })
8061                .collect(),
8062            node,
8063            ancestry,
8064        );
8065
8066        let namespace_scope = ScopeInfo {
8067            package_name,
8068            module,
8069            // C++ never nests a namespace inside a class, so a surviving
8070            // class_unit here is always recovery bleed: a malformed-region
8071            // boundary upstream mis-scoped this namespace block. Keeping the
8072            // owner would mint the namespace's declarations as class members
8073            // under a re-appended package, desyncing the fq boundary assert
8074            // (#2306). Dropping it is identity-neutral for valid code, where
8075            // class_unit is always empty at a namespace definition.
8076            class_unit: None,
8077            template_signature: scope.template_signature.clone(),
8078            template_metadata: scope.template_metadata.clone(),
8079            declarations_are_fields: false,
8080            recovered_specialization_member_scope: false,
8081            visible_using_namespaces: scope.visible_using_namespaces.clone(),
8082            local_block: None,
8083            visible_using_declarations: scope.visible_using_declarations.clone(),
8084        };
8085        let container = cpp_body_node(node).unwrap_or(node);
8086        // A malformed export-macro class body may turn the following class
8087        // into a descendant of a bogus function/labeled/error envelope. Those
8088        // descendants are not declaration containers and the ordinary walk
8089        // intentionally does not descend into them. Scan the namespace tree
8090        // once for the strict embedded class geometry before scheduling its
8091        // normal declarations. When one envelope matches, its helper recovers
8092        // every embedded class and the walk need not inspect its descendants.
8093        let mut candidates = vec![container];
8094        while let Some(candidate) = candidates.pop() {
8095            if matches!(
8096                candidate.kind(),
8097                "ERROR" | "function_definition" | "labeled_statement"
8098            ) && self.visit_embedded_function_like_export_classes(
8099                candidate,
8100                &namespace_scope,
8101                stack,
8102                ancestry,
8103            ) {
8104                continue;
8105            }
8106            for index in (0..candidate.named_child_count()).rev() {
8107                candidates.push(
8108                    candidate
8109                        .named_child(index)
8110                        .expect("index below the node's own named child count"),
8111                );
8112            }
8113        }
8114        stack.push(CppWork::Container(CppContainer {
8115            node: container,
8116            scope: namespace_scope,
8117        }));
8118    }
8119
8120    fn visit_class_like<'tree>(
8121        &mut self,
8122        node: Node<'tree>,
8123        scope: &ScopeInfo,
8124        stack: &mut Vec<CppWork<'tree>>,
8125        ancestry: &ParentIndex<'tree>,
8126    ) {
8127        let Some(name) = class_like_name(node, self.source, ancestry) else {
8128            return;
8129        };
8130        let name = qualified_class_name_chain(node, self.source, scope)
8131            .map(|chain| chain.join("$"))
8132            .unwrap_or(name);
8133        self.visit_named_class_like(node, name, scope, stack, ancestry);
8134    }
8135
8136    fn visit_named_class_like<'tree>(
8137        &mut self,
8138        node: Node<'tree>,
8139        name: String,
8140        scope: &ScopeInfo,
8141        stack: &mut Vec<CppWork<'tree>>,
8142        ancestry: &ParentIndex<'tree>,
8143    ) {
8144        let body = cpp_body_node(node);
8145        let definition_body_present = body.is_some();
8146        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8147            .then(|| extract_cpp_supertypes(node, self.source));
8148        self.visit_named_class_like_shape(
8149            node,
8150            name,
8151            body,
8152            definition_body_present,
8153            None,
8154            raw_supertypes,
8155            Some(extract_cpp_base_facts(node, self.source)),
8156            scope,
8157            stack,
8158            ancestry,
8159        );
8160    }
8161
8162    /// Whether this class-like declaration is a C tag that belongs to the
8163    /// enclosing non-aggregate scope rather than to the aggregate it is
8164    /// lexically written inside.
8165    ///
8166    /// `class_specifier` is deliberately excluded: `class` is not C, so text
8167    /// that spells one in a `.c` file is not C code and keeps the C++ reading
8168    /// rather than getting a half-C identity.
8169    fn mints_tag_at_enclosing_c_scope(
8170        &self,
8171        declaration_node: Node<'_>,
8172        scope: &ScopeInfo,
8173        ancestry: &ParentIndex<'_>,
8174    ) -> bool {
8175        if scope.class_unit.is_none()
8176            || !matches!(
8177                declaration_node.kind(),
8178                "struct_specifier" | "union_specifier" | "enum_specifier"
8179            )
8180            || class_like_name(declaration_node, self.source, ancestry).is_none()
8181        {
8182            return false;
8183        }
8184        // A class-specifier ancestor proves this is C++ syntax. A C reading of
8185        // the header must not apply C tag hoisting to structs nested in that
8186        // class, because that invents a file-scope identity for its members.
8187        let mut ancestor = ancestry.parent(declaration_node);
8188        while let Some(current) = ancestor {
8189            if current.kind() == "class_specifier" {
8190                return false;
8191            }
8192            ancestor = ancestry.parent(current);
8193        }
8194        true
8195    }
8196
8197    #[allow(clippy::too_many_arguments)]
8198    fn visit_named_class_like_shape<'tree>(
8199        &mut self,
8200        declaration_node: Node<'tree>,
8201        name: String,
8202        body: Option<Node<'tree>>,
8203        definition_body_present: bool,
8204        explicit_range: Option<Range>,
8205        raw_supertypes: Option<Vec<String>>,
8206        base_facts: Option<Vec<CppBaseSpecifierFact>>,
8207        scope: &ScopeInfo,
8208        stack: &mut Vec<CppWork<'tree>>,
8209        ancestry: &ParentIndex<'tree>,
8210    ) -> CodeUnit {
8211        let displaced_macro_tail = if explicit_range.is_none() {
8212            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8213        } else {
8214            None
8215        };
8216        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8217        let recovered_scope = self.scope_for_recovered_exported_class(
8218            declaration_node,
8219            &name,
8220            definition_body_present,
8221            scope,
8222            ancestry,
8223        );
8224        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
8225        // aggregate's member list is declared at the enclosing non-aggregate
8226        // scope, not nested inside the aggregate. `scope.class_unit` is the
8227        // only aggregate carrier in this walk, so dropping it puts the tag at
8228        // the nearest enclosing non-aggregate scope -- the module at file or
8229        // namespace scope, and the same block-scope representation a
8230        // function-local aggregate already gets. The tag's own body scope
8231        // below still owns its members, so fields and enumerators are
8232        // unaffected.
8233        let c_tag_scope;
8234        let c_tag_scope_applies =
8235            self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8236        if !self.c_tag_semantics && c_tag_scope_applies {
8237            self.c_tag_scope_witness = true;
8238        }
8239        let scope = if self.c_tag_semantics && c_tag_scope_applies {
8240            c_tag_scope = ScopeInfo {
8241                class_unit: None,
8242                ..recovered_scope.clone()
8243            };
8244            &c_tag_scope
8245        } else {
8246            &recovered_scope
8247        };
8248        let local_block = scope
8249            .local_block
8250            .as_ref()
8251            .filter(|_| scope.class_unit.is_none());
8252        let short_name = if let Some(parent) = &scope.class_unit {
8253            cpp_join_nested_short(parent.short_name(), &name)
8254        } else if let Some(block) = local_block {
8255            cpp_join_nested_short(&block.short_name, &name)
8256        } else {
8257            name.clone()
8258        };
8259        // A top-level out-of-line qualified class definition (`struct
8260        // Outer::Inner { ... }` inside its namespace, #2246) carries its
8261        // nesting chain as the `$`-joined display name; push one Type/Nested
8262        // segment per class so segment-pop owner navigation keeps working.
8263        // Every other leaf name stays opaque so a literal `$` in a source
8264        // identifier never crosses the split/join boundary (#2140).
8265        let qualified_chain = if scope.class_unit.is_none() && local_block.is_none() {
8266            qualified_class_name_chain(declaration_node, self.source, scope)
8267                .filter(|chain| chain.join("$") == name)
8268        } else {
8269            None
8270        };
8271        let fq = if let Some(block) = local_block {
8272            block
8273                .fq
8274                .clone()
8275                .with_pushed(segment_interner().intern(&name, SegmentKind::Nested))
8276        } else if let Some(chain) = qualified_chain {
8277            let mut fq = FqName::new();
8278            cpp_push_package(&mut fq, &scope.package_name);
8279            let mut first = true;
8280            for component in chain {
8281                let kind = if first {
8282                    SegmentKind::Type
8283                } else {
8284                    SegmentKind::Nested
8285                };
8286                fq.push(segment_interner().intern(&component, kind));
8287                first = false;
8288            }
8289            fq
8290        } else {
8291            cpp_leaf_fq(
8292                &scope.package_name,
8293                scope.class_unit.as_ref(),
8294                &name,
8295                SegmentKind::Nested,
8296                SegmentKind::Type,
8297            )
8298        };
8299        let code_unit = CodeUnit::with_signature_and_fq(
8300            self.file.clone(),
8301            CodeUnitType::Class,
8302            scope.package_name.clone(),
8303            short_name,
8304            scope.template_signature.clone(),
8305            false,
8306            fq,
8307        );
8308        let has_body = definition_body_present;
8309        if !has_body && self.parsed.contains_declaration(&code_unit) {
8310            if let Some(range) = explicit_range {
8311                self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8312            } else {
8313                self.link_source_declaration(ancestry, &code_unit, declaration_node);
8314            }
8315            self.parsed.record_navigation_range(
8316                code_unit.clone(),
8317                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8318            );
8319            return code_unit;
8320        }
8321        if has_body {
8322            // A forward declaration above this definition is one of the
8323            // class's declaration sites: C++ injects the class name at the
8324            // forward declaration, so a reference between the two spells this
8325            // class (#3297). Moving the unit to its body must not drop the
8326            // earlier site, exactly as a function prototype and its body stay
8327            // two ranges of one declaration (#1650).
8328            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8329            if let Some(range) = explicit_range {
8330                self.replace_declaration_with_range_deferred(
8331                    ancestry,
8332                    code_unit.clone(),
8333                    range,
8334                    Some(declaration_node),
8335                    None,
8336                    None,
8337                );
8338            } else {
8339                self.replace_declaration_deferred(
8340                    ancestry,
8341                    code_unit.clone(),
8342                    declaration_node,
8343                    None,
8344                    None,
8345                );
8346            }
8347            for range in earlier_ranges {
8348                self.parsed.add_declaration_range(&code_unit, range);
8349            }
8350        } else {
8351            self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8352        }
8353        if let Some(declaration) = self
8354            .source_links
8355            .get(&code_unit)
8356            .and_then(|links| links.last())
8357            .copied()
8358        {
8359            let mut properties = self.source_properties.borrow_mut();
8360            let fact = properties
8361                .get_mut(&declaration)
8362                .expect("linked class has source properties");
8363            fact.occurrence_role = if has_body {
8364                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8365            } else {
8366                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8367            };
8368            fact.class_strength = if has_body {
8369                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8370            } else {
8371                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8372            };
8373            if let Some(base_facts) = base_facts.as_ref() {
8374                fact.bases = base_facts.clone();
8375            }
8376        }
8377        if let Some(raw_supertypes) = raw_supertypes {
8378            self.parsed
8379                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8380        }
8381        self.parsed.add_signature(
8382            code_unit.clone(),
8383            render_cpp_type_signature(
8384                declaration_node,
8385                self.source,
8386                scope.template_signature.as_deref(),
8387            ),
8388        );
8389        if let Some(metadata) = &scope.template_metadata {
8390            let primary_short_name = if let Some(parent) = &scope.class_unit {
8391                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8392            } else {
8393                metadata.primary_name.clone()
8394            };
8395            let primary_fq_name = CodeUnit::new(
8396                self.file.clone(),
8397                CodeUnitType::Class,
8398                scope.package_name.clone(),
8399                primary_short_name,
8400            )
8401            .fq_name();
8402            let mut metadata = metadata.clone();
8403            metadata.primary_fq_name = primary_fq_name;
8404            self.parsed
8405                .set_cpp_template_metadata(code_unit.clone(), metadata);
8406        }
8407        if let Some(parent) = &scope.class_unit {
8408            self.parsed.add_child(parent.clone(), code_unit.clone());
8409        } else if let Some(block) = local_block {
8410            self.parsed
8411                .add_child(block.callable.clone(), code_unit.clone());
8412        } else if let Some(module) = &scope.module {
8413            self.parsed.add_child(module.clone(), code_unit.clone());
8414        }
8415
8416        if let Some(body) = body {
8417            let mut nested_scope = scope.clone();
8418            nested_scope.class_unit = Some(code_unit.clone());
8419            nested_scope.local_block = None;
8420            nested_scope.template_signature = scope.template_signature.clone();
8421            // Template metadata describes the class just created. It must not
8422            // leak into ordinary nested declarations in that class's body.
8423            // Recovered export-macro specializations carry a separate scope bit
8424            // for their declaration-shaped body members.
8425            nested_scope.template_metadata = None;
8426            // Export-macro class bodies recovered from a function_definition use
8427            // compound_statement children, whose direct fields are declarations.
8428            nested_scope.recovered_specialization_member_scope =
8429                scope.template_metadata.as_ref().is_some_and(|metadata| {
8430                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
8431                });
8432            nested_scope.declarations_are_fields =
8433                is_recovered_exported_class_container(declaration_node, self.source)
8434                    || nested_scope.recovered_specialization_member_scope;
8435            if let Some(displaced) = displaced_macro_tail {
8436                // A macro-shaped field without a source semicolon can make
8437                // tree-sitter consume the real class terminator as an ERROR
8438                // inside that field, then retain following namespace items as
8439                // later field-list children. Drain the proven class prefix
8440                // first and re-own only the structured tail with the outer
8441                // scope. The tail is pushed first because the work stack is
8442                // LIFO.
8443                push_cpp_sibling_range(
8444                    body,
8445                    displaced.split_index,
8446                    usize::MAX,
8447                    scope.clone(),
8448                    stack,
8449                );
8450                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8451            } else {
8452                stack.push(CppWork::Container(CppContainer {
8453                    node: body,
8454                    scope: nested_scope,
8455                }));
8456            }
8457        }
8458        if declaration_node.kind() == "enum_specifier" {
8459            self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8460        }
8461        code_unit
8462    }
8463
8464    fn visit_enum_enumerators(
8465        &mut self,
8466        ancestry: &ParentIndex<'_>,
8467        node: Node<'_>,
8468        scope: &ScopeInfo,
8469        parent: &CodeUnit,
8470    ) {
8471        walk_named_tree_preorder(node, false, |child| {
8472            if child.kind() != "enumerator" {
8473                return WalkControl::Continue;
8474            }
8475            let Some(name_node) = child.child_by_field_name("name") else {
8476                return WalkControl::Continue;
8477            };
8478            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8479            if name.is_empty() {
8480                return WalkControl::Continue;
8481            }
8482            let signature = normalize_cpp_whitespace(node_text(child, self.source));
8483            self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8484            WalkControl::Continue
8485        });
8486    }
8487
8488    fn visit_enum_enumerator(
8489        &mut self,
8490        ancestry: &ParentIndex<'_>,
8491        declaration_node: Node<'_>,
8492        scope: &ScopeInfo,
8493        parent: &CodeUnit,
8494        name: &str,
8495        signature: &str,
8496    ) {
8497        let code_unit = CodeUnit::new_fq(
8498            self.file.clone(),
8499            CodeUnitType::Field,
8500            scope.package_name.clone(),
8501            cpp_join_member_short(parent.short_name(), name),
8502            parent
8503                .fq()
8504                .clone()
8505                .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8506        );
8507        if self.parsed.contains_declaration(&code_unit) {
8508            return;
8509        }
8510        self.add_declaration(
8511            ancestry,
8512            code_unit.clone(),
8513            declaration_node,
8514            Some(parent.clone()),
8515            None,
8516        );
8517        self.parsed.add_signature(code_unit, signature.to_string());
8518    }
8519
8520    fn visit_function_definition<'tree>(
8521        &mut self,
8522        node: Node<'tree>,
8523        scope: &ScopeInfo,
8524        stack: &mut Vec<CppWork<'tree>>,
8525        ancestry: &ParentIndex<'tree>,
8526    ) {
8527        // An attribute-like macro invocation whose argument is a declaration can
8528        // swallow every declaration written after it into one bogus
8529        // `function_definition` (#2551). This owns the whole region, so it runs
8530        // ahead of `visit_macro_swallowed_function_declarations` below, which
8531        // admits the same envelope by its head macro token and reads single
8532        // declarators out of nodes this recovery reparses properly.
8533        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8534            return;
8535        }
8536        // A file-scope object-like macro sentinel the parser cannot see (issue
8537        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
8538        // prefixes as a bogus `function_definition` that swallows real namespaces,
8539        // classes, and members. Reparse the swallowed interior as C++ items so the
8540        // ordinary declaration visitors index it with byte/line-exact ownership.
8541        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8542            return;
8543        }
8544        // The omitted-semicolon field near miss (`DWORD preserved_field` ahead
8545        // of `void PreserveFollowingFunction() {}`): the macro-declaration
8546        // grammar folds the pair into the function's head as two leading
8547        // macro_modifiers where the older grammar left a damaged
8548        // field_declaration. Index the field; the function itself continues
8549        // through the ordinary path below.
8550        if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8551            self.visit_omitted_semicolon_macro_field(
8552                node,
8553                type_modifier,
8554                name_modifier,
8555                scope,
8556                ancestry,
8557            );
8558        }
8559        // The macro-return stand-in (`CUTLASS_DEVICE static OperandLayout
8560        // Layout() { ... }`): the same clean envelope, but the return type
8561        // names an active template type parameter, and the older grammar's
8562        // phantom field was the definition `OperandLayout::packed` resolved to
8563        // (#1129). Recover that field from the clean head.
8564        if scope.class_unit.is_some()
8565            && let Some((head_start, type_node)) =
8566                macro_return_template_parameter_field(node, self.source, ancestry)
8567        {
8568            self.visit_macro_return_template_parameter_field(
8569                node, head_start, type_node, scope, ancestry,
8570            );
8571        }
8572        if node.has_error() {
8573            self.visit_macro_swallowed_function_declarations(node, scope);
8574        }
8575        if let Some((class_node, name, raw_supertypes, base_facts)) =
8576            recover_exported_class_function_definition(node, self.source)
8577        {
8578            if let Some(body) = cpp_body_node(node)
8579                && let Some(close) = self
8580                    .orphaned_namespaces
8581                    .matching_close_brace(body.start_byte())
8582                && close.end_byte < body.end_byte()
8583            {
8584                let class_range = Range {
8585                    start_byte: node.start_byte(),
8586                    end_byte: close.end_byte,
8587                    start_line: node.start_position().row + 1,
8588                    end_line: close.end_line,
8589                };
8590                // Parse the body in class context, preserving the original
8591                // keyword, recovered name and body as included ranges. Parsing
8592                // the interior at file scope turns constructors and access
8593                // labels into another oversized ERROR instead of members.
8594                let mut head = vec![node];
8595                let mut keyword = None;
8596                let mut name_node = None;
8597                while let Some(part) = head.pop() {
8598                    if part.start_byte() >= body.start_byte() || part.is_missing() {
8599                        continue;
8600                    }
8601                    if matches!(part.kind(), "class" | "struct" | "union") {
8602                        keyword = Some(part);
8603                    }
8604                    if matches!(part.kind(), "identifier" | "type_identifier")
8605                        && node_text(part, self.source) == name
8606                    {
8607                        name_node = Some(part);
8608                    }
8609                    let mut cursor = part.walk();
8610                    head.extend(part.children(&mut cursor));
8611                }
8612                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8613                    && let Some(type_name) = keyword
8614                        .parent()
8615                        .and_then(|parent| parent.child_by_field_name("name"))
8616                    && let Some(tree) = parse_source_ranges_with_cancellation(
8617                        &tree_sitter_cpp::LANGUAGE.into(),
8618                        self.source,
8619                        &[
8620                            (keyword.start_byte(), type_name.start_byte()),
8621                            (name_node.start_byte(), name_node.end_byte()),
8622                            (body.start_byte(), close.end_byte),
8623                        ],
8624                        None,
8625                    )
8626                    && let Some(reparsed_class) = tree.root_node().named_child(0)
8627                    && let Some(class_body) = cpp_body_node(reparsed_class)
8628                    && class_body.start_byte() == body.start_byte()
8629                    && class_body.end_byte() == close.end_byte
8630                    && let Some(tail) =
8631                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8632                {
8633                    let class_body_range = class_body.byte_range();
8634                    self.with_recovery_source_context(node, |visitor| {
8635                        let source_context = visitor.active_source_context.clone();
8636                        let class_unit = visitor.visit_named_class_like_shape(
8637                            class_node,
8638                            name,
8639                            None,
8640                            true,
8641                            Some(class_range),
8642                            raw_supertypes,
8643                            base_facts,
8644                            scope,
8645                            stack,
8646                            ancestry,
8647                        );
8648                        visitor.parsed.record_materialization(
8649                            MaterializationRecord::RecoveredDeclaration {
8650                                recovery: class_range,
8651                                unit: class_unit.clone(),
8652                            },
8653                        );
8654                        let member_scope = ScopeInfo {
8655                            package_name: class_unit.package_name().to_string(),
8656                            class_unit: Some(class_unit),
8657                            declarations_are_fields: true,
8658                            template_metadata: None,
8659                            recovered_specialization_member_scope: false,
8660                            ..scope.clone()
8661                        };
8662                        let tail_range = tail.root_node().byte_range();
8663                        let class_body_recovery = cpp_recovery_window(
8664                            visitor.source,
8665                            class_body_range.start,
8666                            class_body_range.end,
8667                        );
8668                        let tail_recovery =
8669                            cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8670                        visitor.partitioned_regions.push(CppPartitionedRegion {
8671                            tree: tail,
8672                            range: tail_range,
8673                            scope: scope.clone(),
8674                            recovery: tail_recovery,
8675                            source_context: source_context.clone(),
8676                        });
8677                        visitor.partitioned_regions.push(CppPartitionedRegion {
8678                            tree,
8679                            range: class_body_range,
8680                            scope: member_scope,
8681                            recovery: class_body_recovery,
8682                            source_context: source_context.clone(),
8683                        });
8684                    });
8685                    return;
8686                }
8687            }
8688            let body = cpp_body_node(class_node);
8689            let displaced_namespace = cpp_body_node(node)
8690                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8691            let fragmented = cpp_body_node(node).and_then(|body| {
8692                fragmented_export_function_body_region(
8693                    node,
8694                    body,
8695                    self.source,
8696                    displaced_namespace.as_ref(),
8697                )
8698            });
8699            // The recovery tuple's first node is the class-like type when the
8700            // parser exposes one, but the synthetic wrapper owns the compound
8701            // statement that contains the truncated class body. Use the
8702            // wrapper body for fragmented-member detection; retain the
8703            // class-node body for the ordinary (non-fragmented) path below.
8704            if let Some(fragmented) = fragmented {
8705                // The lifted sibling no longer sits below the parser-visible
8706                // namespace node. Restore the current parent scope when the
8707                // ordinary work walk reaches that class.
8708                if let Some(boundary) =
8709                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8710                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8711                {
8712                    let mut boundary_scope = scope.clone();
8713                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8714                        if sibling.start_byte() >= boundary.start_byte() {
8715                            break;
8716                        }
8717                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8718                            boundary_scope.visible_using_namespaces.push(namespace);
8719                        }
8720                        push_cpp_using_declaration_binding(
8721                            &mut boundary_scope,
8722                            sibling,
8723                            self.source,
8724                        );
8725                    }
8726                    self.recovered_class_sibling_scopes
8727                        .insert(boundary.id(), boundary_scope);
8728                }
8729                let mut recovered_constructor = None;
8730                let mut recovered_prefix_tree = None;
8731                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8732                {
8733                    Some(FragmentedExportMembers::Complete(tree)) => {
8734                        if let Some(body) = body
8735                            && let Some(range) =
8736                                cpp_reparsed_synthetic_initializer_constructor_range(
8737                                    tree.root_node(),
8738                                    &name,
8739                                    self.source,
8740                                    body.end_byte(),
8741                                )
8742                        {
8743                            recovered_constructor = Some(range);
8744                            recovered_prefix_tree = Some(tree);
8745                            None
8746                        } else {
8747                            Some(FragmentedExportMembers::Complete(tree))
8748                        }
8749                    }
8750                    outcome => outcome,
8751                };
8752                let mut class_stack = Vec::new();
8753                let class_unit = self.visit_named_class_like_shape(
8754                    class_node,
8755                    name,
8756                    None,
8757                    true,
8758                    Some(fragmented.class_range),
8759                    raw_supertypes,
8760                    base_facts.clone(),
8761                    scope,
8762                    &mut class_stack,
8763                    ancestry,
8764                );
8765                self.parsed
8766                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
8767                        recovery: fragmented.class_range,
8768                        unit: class_unit.clone(),
8769                    });
8770                let complete = outcome.is_some_and(|outcome| {
8771                    self.visit_fragmented_export_class_members(
8772                        node,
8773                        outcome,
8774                        class_unit.clone(),
8775                        scope,
8776                    )
8777                });
8778                if complete {
8779                    self.consumed_fragment_regions
8780                        .push((node.start_byte(), fragmented.class_range.end_byte));
8781                } else {
8782                    // The reparse can fail when the first constructor or a
8783                    // method body is split into statement-shaped siblings.
8784                    // Keep the recovered class envelope, but do not visit the
8785                    // synthetic wrapper body: its initializer expressions can
8786                    // look like same-named member functions (for example
8787                    // `Token.location(loc)`). Re-own only the original sibling
8788                    // nodes that fall inside the proven class range. Their CST
8789                    // shapes retain the real field/function kinds and ranges.
8790                    let member_scope = ScopeInfo {
8791                        package_name: class_unit.package_name().to_string(),
8792                        module: scope.module.clone(),
8793                        class_unit: Some(class_unit.clone()),
8794                        template_signature: scope.template_signature.clone(),
8795                        template_metadata: None,
8796                        declarations_are_fields: true,
8797                        recovered_specialization_member_scope: false,
8798                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
8799                        local_block: None,
8800                        visible_using_declarations: scope.visible_using_declarations.clone(),
8801                    };
8802                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8803                        if candidate.start_byte() >= fragmented.reparse_end {
8804                            break;
8805                        }
8806                        if cpp_fragment_sibling_is_class_member(
8807                            candidate,
8808                            fragmented.reparse_end,
8809                            self.source,
8810                        ) {
8811                            self.recovered_class_sibling_scopes
8812                                .insert(candidate.id(), member_scope.clone());
8813                        }
8814                    }
8815                    if let Some(range) = recovered_constructor
8816                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8817                    {
8818                        let prefix_root = prefix_tree.root_node();
8819                        let prefix_ancestry = self.recovery_ancestry(prefix_root);
8820                        self.visit_recovered_fragment_prefix_members(
8821                            node,
8822                            prefix_root,
8823                            range.start,
8824                            &class_unit,
8825                            scope,
8826                            &prefix_ancestry,
8827                        );
8828                        self.visit_recovered_fragment_constructor(
8829                            range,
8830                            body,
8831                            class_node,
8832                            &class_unit,
8833                            scope,
8834                            ancestry,
8835                        );
8836                    }
8837                }
8838                if let Some(boundary) = displaced_namespace {
8839                    for item in boundary.namespace_items {
8840                        self.recovered_class_sibling_scopes
8841                            .insert(item.id(), scope.clone());
8842                    }
8843                }
8844                stack.extend(class_stack);
8845                return;
8846            }
8847            let mut stack = Vec::new();
8848            let class_unit = self.visit_named_class_like_shape(
8849                class_node,
8850                name,
8851                body,
8852                body.is_some(),
8853                None,
8854                raw_supertypes,
8855                base_facts,
8856                scope,
8857                &mut stack,
8858                ancestry,
8859            );
8860            self.parsed
8861                .record_materialization(MaterializationRecord::RecoveredDeclaration {
8862                    recovery: cpp_declaration_range(node),
8863                    unit: class_unit,
8864                });
8865            // Issue #1524: the bogus `function_definition` body can run past
8866            // the class's true closing brace (the parse ends it with a
8867            // zero-width `MISSING "}"`), swallowing following namespace-scope
8868            // siblings -- they would index as members of the recovered class.
8869            // When the body's text-balanced close lands before the body's own
8870            // end, re-own the swallowed tail with the outer scope instead.
8871            if let Some(body) = body
8872                && let Some(class_close) = self
8873                    .orphaned_namespaces
8874                    .matching_close_brace(body.start_byte())
8875                && class_close.start_byte < body.end_byte()
8876            {
8877                let split = {
8878                    let mut cursor = body.walk();
8879                    body.named_children(&mut cursor)
8880                        .position(|child| child.start_byte() > class_close.start_byte)
8881                };
8882                if let Some(split) = split {
8883                    // The seeded work is a single Container over the whole
8884                    // body with the class scope; replace it with the bounded
8885                    // head (class scope) plus the swallowed tail (outer
8886                    // scope). Push tail first so the head drains first.
8887                    let seeded = stack.pop();
8888                    match seeded {
8889                        Some(CppWork::Container(container)) => {
8890                            push_cpp_sibling_range(
8891                                body,
8892                                split,
8893                                usize::MAX,
8894                                scope.clone(),
8895                                &mut stack,
8896                            );
8897                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8898                        }
8899                        // visit_named_class_like_shape always seeds exactly
8900                        // one Container when a body is present.
8901                        _ => unreachable!("exported-class seed is always one Container"),
8902                    }
8903                }
8904            }
8905            while let Some(work) = stack.pop() {
8906                match work {
8907                    CppWork::Container(container) => {
8908                        push_cpp_container_work(container.node, container.scope, &mut stack);
8909                    }
8910                    CppWork::Siblings(siblings) => {
8911                        advance_cpp_siblings(siblings, self.source, &mut stack);
8912                    }
8913                    CppWork::Node(work) => {
8914                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8915                    }
8916                }
8917            }
8918            return;
8919        }
8920        let recovered_constraint_constructor =
8921            cpp_recovered_template_macro_constructor(node, self.source);
8922        // A constraint-macro template head the parser split off keeps the
8923        // function's declared type parameter, so the member stays a template.
8924        let constrained_scope;
8925        let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
8926            Some(parameter) => {
8927                constrained_scope = ScopeInfo {
8928                    template_signature: Some(format!(
8929                        "<{}>",
8930                        normalize_cpp_whitespace(node_text(parameter, self.source))
8931                    )),
8932                    ..scope.clone()
8933                };
8934                &constrained_scope
8935            }
8936            None => scope,
8937        };
8938        let declarator = recovered_constraint_constructor
8939            .map(|(declarator, _)| declarator)
8940            .or_else(|| {
8941                scope.class_unit.as_ref().and_then(|class| {
8942                    recovered_macro_decorated_constructor_declarator(
8943                        node,
8944                        class.identifier(),
8945                        self.source,
8946                    )
8947                })
8948            })
8949            .or_else(|| node.child_by_field_name("declarator"));
8950        let Some(declarator) = declarator else {
8951            self.visit_malformed_function_definition_container(node, scope, stack);
8952            return;
8953        };
8954        let Some(function_declarator) = extract_function_declarator(declarator) else {
8955            self.visit_malformed_function_definition_container(node, scope, stack);
8956            return;
8957        };
8958        let function = if let Some((_, callable_name)) =
8959            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8960        {
8961            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8962        } else {
8963            extract_function_info(function_declarator, self.source, scope)
8964        };
8965        let Some(mut function) = function else {
8966            self.visit_malformed_function_definition_container(node, scope, stack);
8967            return;
8968        };
8969        if let Some((_, template_parameter)) = recovered_constraint_constructor {
8970            function.signature = format!(
8971                "template <{}>{}",
8972                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8973                function.signature
8974            );
8975        }
8976        let code_unit = function.code_unit(self.file.clone());
8977        // Keep an earlier same-file prototype as another physical occurrence
8978        // of this callable. `CodeUnit` already identifies the role-neutral
8979        // overload, while ranges and signature metadata describe its
8980        // declaration/definition occurrences.
8981        self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8982        let signature = if recovered_constraint_constructor.is_some() {
8983            normalize_cpp_whitespace(node_text(function_declarator, self.source))
8984        } else {
8985            render_cpp_function_display_signature_from_node(
8986                node,
8987                self.source,
8988                scope.template_signature.as_deref(),
8989                true,
8990                ancestry,
8991            )
8992        };
8993        self.add_signature_with_metadata(
8994            code_unit.clone(),
8995            cpp_signature_metadata(
8996                signature,
8997                function_declarator,
8998                cpp_callable_linkage(node, self.source, ancestry),
8999                self.source,
9000                ancestry,
9001            )
9002            .with_declaration_only(false),
9003        );
9004        if let Some(parent) = &scope.class_unit {
9005            self.parsed.add_child(parent.clone(), code_unit.clone());
9006        } else if let Some(module) = &scope.module {
9007            self.parsed.add_child(module.clone(), code_unit.clone());
9008        }
9009        self.visit_local_types_in_function(node, &code_unit, scope, stack, ancestry);
9010    }
9011
9012    /// Recover the namespace lost when tree-sitter promotes an export-macro
9013    /// class definition to a root-level `function_definition`.  Only a
9014    /// body-bearing, top-level recovery may borrow a namespace, and only when
9015    /// one earlier namespace-scope forward declaration proves the identity.
9016    fn scope_for_recovered_exported_class<'tree>(
9017        &mut self,
9018        node: Node<'tree>,
9019        name: &str,
9020        definition_body_present: bool,
9021        scope: &ScopeInfo,
9022        ancestry: &ParentIndex<'tree>,
9023    ) -> ScopeInfo {
9024        if !definition_body_present
9025            || !scope.package_name.is_empty()
9026            || scope.class_unit.is_some()
9027            || !(is_recovered_exported_class_container(node, self.source)
9028                || recover_function_like_export_class_pair(node, self.source).is_some()
9029                || recover_embedded_function_like_export_classes(node, self.source)
9030                    .iter()
9031                    .any(|recovered| recovered.name == name)
9032                || matches!(node.kind(), "declaration" | "field_declaration")
9033                    && recover_exported_class_declaration(node, self.source).is_some()
9034                || matches!(
9035                    node.kind(),
9036                    "class_specifier" | "struct_specifier" | "union_specifier"
9037                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
9038                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
9039                        name_node,
9040                        self.source,
9041                    )))
9042                }) || ancestry.parent(node).is_some_and(|parent| {
9043                    matches!(parent.kind(), "declaration" | "field_declaration")
9044                        && recover_exported_class_declaration(parent, self.source).is_some()
9045                        || is_recovered_exported_class_container(parent, self.source)
9046                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
9047        {
9048            return scope.clone();
9049        }
9050        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
9051        let Some(package_name) = borrowed_namespace
9052            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
9053        else {
9054            return scope.clone();
9055        };
9056
9057        let module = CodeUnit::new_fq(
9058            self.file.clone(),
9059            CodeUnitType::Module,
9060            "",
9061            package_name.clone(),
9062            cpp_namespace_fq(&package_name),
9063        );
9064        let mut recovered = scope.clone();
9065        recovered.package_name = package_name;
9066        recovered.module = Some(module);
9067        recovered
9068    }
9069
9070    /// The unique namespace-scope forward declaration of `name` that precedes
9071    /// `recovered_node`, answered from the walk's carried-forward scan of the
9072    /// tree `recovered_node` belongs to.
9073    ///
9074    /// The scan is built on the first question and advanced by each later one,
9075    /// so a file that never reaches this path -- almost every file -- pays
9076    /// nothing, and one that reaches it thousands of times pays a single pass
9077    /// (#2754).
9078    fn unique_earlier_namespace_forward<'tree>(
9079        &mut self,
9080        recovered_node: Node<'tree>,
9081        name: &str,
9082        ancestry: &ParentIndex<'tree>,
9083    ) -> Option<String> {
9084        let mut root = recovered_node;
9085        while let Some(parent) = ancestry.parent(root) {
9086            root = parent;
9087        }
9088        let source = self.source;
9089        let scan = self
9090            .namespace_forward_scans
9091            .entry(CppTreeIdentity::of(root))
9092            .or_default();
9093        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
9094        let borrowed = scan.unique_earlier_forward(name, recovered_node);
9095
9096        #[cfg(debug_assertions)]
9097        assert_eq!(
9098            borrowed,
9099            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
9100            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
9101             for {name} at byte {}",
9102            recovered_node.start_byte()
9103        );
9104
9105        borrowed
9106    }
9107
9108    fn visit_malformed_function_definition_container<'tree>(
9109        &mut self,
9110        node: Node<'tree>,
9111        scope: &ScopeInfo,
9112        stack: &mut Vec<CppWork<'tree>>,
9113    ) {
9114        let Some(body) = cpp_body_node(node) else {
9115            return;
9116        };
9117        if !cpp_contains_namespace_definition(body) {
9118            return;
9119        }
9120        stack.push(CppWork::Container(CppContainer {
9121            node: body,
9122            scope: scope.clone(),
9123        }));
9124    }
9125
9126    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
9127    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
9128    /// emits for a sentinel-prefixed region, reparse the interior after the
9129    /// sentinel identifier as real C++ items -- confined to the region so
9130    /// every reparsed node keeps its original byte/line position -- and run the
9131    /// ordinary container visitation over the result. Returns `true` when it fired
9132    /// (the caller must then skip normal function processing). Nested sentinel
9133    /// regions recover recursively: the reparsed interior is walked through the
9134    /// same `visit_function_definition` path, so a sentinel inside the region hits
9135    /// this recovery again.
9136    /// Runs `reparse_walk` and records every declaration it mints as a
9137    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
9138    /// `recovery` (issue #1657). A reparsed sentinel region has no single
9139    /// recovered envelope unit: the ordinary visitors mint namespaces,
9140    /// classes, and members directly from the reparsed tree, so the walk's
9141    /// declaration delta is the recovered set. Records are ordered by
9142    /// declaration start byte so the parse product stays deterministic.
9143    fn record_recovered_declarations(
9144        &mut self,
9145        recovery: Range,
9146        origin: Node<'_>,
9147        reparse_walk: impl FnOnce(&mut Self),
9148    ) {
9149        // The recovered declarations begin at the recovery start, which can lie
9150        // past a branch directive the malformed origin swallowed (#6325:
9151        // `END_NS #else BEGIN_NS class C {};` parses as one declaration inside
9152        // the `#if` branch). Read their preprocessor context there.
9153        let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9154            && recovery.start_byte < origin.end_byte())
9155        .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9156        .flatten()
9157        .unwrap_or(origin);
9158        self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9159            let source_context = visitor.active_source_context.clone();
9160            visitor.record_recovered_declarations_with_context(
9161                recovery,
9162                source_context,
9163                reparse_walk,
9164            );
9165        });
9166    }
9167
9168    /// Run a recovered walk with an owned source context and record every
9169    /// declaration it mints. Deferred partitioned regions cannot retain their
9170    /// origin `Node`, so they capture the context while the origin tree is
9171    /// live and use this same materialization path when drained.
9172    fn record_recovered_declarations_with_context(
9173        &mut self,
9174        recovery: Range,
9175        source_context: Option<CppActiveSourceContext>,
9176        reparse_walk: impl FnOnce(&mut Self),
9177    ) {
9178        // The set difference this used to be, kept as the oracle every answer
9179        // is asserted against (#2787).
9180        #[cfg(any(debug_assertions, test))]
9181        let before = self.parsed.declarations().clone();
9182
9183        self.recovery_captures.push(CppRecoveryCapture::default());
9184        let previous = self.active_source_context.take();
9185        self.active_source_context = source_context;
9186        reparse_walk(self);
9187        self.active_source_context = previous;
9188        let captured = self
9189            .recovery_captures
9190            .pop()
9191            .expect("the capture this call pushed is the one it pops");
9192
9193        // The capture holds every declaration created while it was open, once
9194        // each and in creation order, so the recovered set costs what the
9195        // recovery made rather than everything the file has declared so far.
9196        // One filter is left to apply: a created declaration that a later
9197        // deferred replacement removed is not in the parse product to report.
9198        let mut minted: Vec<CodeUnit> = captured
9199            .created
9200            .into_iter()
9201            .filter(|unit| self.parsed.contains_declaration(unit))
9202            .collect();
9203        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9204
9205        #[cfg(any(debug_assertions, test))]
9206        {
9207            let mut rediscovered: Vec<CodeUnit> = self
9208                .parsed
9209                .declarations()
9210                .iter()
9211                .filter(|unit| !before.contains(*unit))
9212                .cloned()
9213                .collect();
9214            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9215            assert_eq!(
9216                minted, rediscovered,
9217                "the captured recovered set must be the declaration delta of the reparse \
9218                 walk over {recovery:?}"
9219            );
9220        }
9221
9222        for unit in minted {
9223            self.parsed
9224                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9225                    recovery,
9226                    unit,
9227                });
9228        }
9229    }
9230
9231    /// Where one recovered declaration sorts: by start byte, then by name, so
9232    /// the parse product stays deterministic.
9233    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9234        let start = self
9235            .parsed
9236            .declaration_ranges(unit)
9237            .first()
9238            .map(|range| range.start_byte)
9239            .unwrap_or(usize::MAX);
9240        (start, unit.fq_name().to_string())
9241    }
9242
9243    fn visit_sentinel_macro_region<'tree>(
9244        &mut self,
9245        node: Node<'tree>,
9246        scope: &ScopeInfo,
9247        stack: &mut Vec<CppWork<'tree>>,
9248        ancestry: &ParentIndex<'tree>,
9249    ) -> bool {
9250        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9251            return true;
9252        }
9253        if let Some((
9254            reparse_start,
9255            class_start,
9256            body_start,
9257            class_close_start,
9258            class_close_end,
9259            class_close_line,
9260        )) = cpp_sentinel_macro_class_region(node, self.source)
9261        {
9262            let Some(class_tree) =
9263                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9264            else {
9265                return false;
9266            };
9267            let class_root = class_tree.root_node();
9268            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9269            // A region reparse is its own tree and needs its own parent index.
9270            let class_ancestry = self.recovery_ancestry(class_root);
9271            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9272                class_root,
9273                template_node,
9274                self.source,
9275                &class_ancestry,
9276            ) else {
9277                return false;
9278            };
9279            let class_node = reparsed_class.declaration_node;
9280            let name = reparsed_class.name;
9281            let mut class_scope = scope.clone();
9282            if let Some(template_node) = template_node {
9283                class_scope.template_signature =
9284                    cpp_template_signature(template_node, class_node, self.source);
9285                class_scope.template_metadata =
9286                    cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9287            }
9288            let Some(body_tree) =
9289                cpp_reparse_region_items(self.source, body_start, class_close_start)
9290            else {
9291                return false;
9292            };
9293            let raw_supertypes = reparsed_class.raw_supertypes;
9294            let base_facts = reparsed_class.base_facts;
9295            let class_range = Range {
9296                start_byte: class_start,
9297                end_byte: class_close_end,
9298                start_line: class_node.start_position().row + 1,
9299                end_line: class_close_line,
9300            };
9301            let class_scope = self.scope_for_recovered_exported_class(
9302                class_node,
9303                &name,
9304                true,
9305                &class_scope,
9306                &class_ancestry,
9307            );
9308            let mut class_stack = Vec::new();
9309            self.with_recovery_source_context(node, |visitor| {
9310                let class_unit = visitor.visit_named_class_like_shape(
9311                    class_node,
9312                    name,
9313                    None,
9314                    true,
9315                    Some(class_range),
9316                    raw_supertypes,
9317                    base_facts,
9318                    &class_scope,
9319                    &mut class_stack,
9320                    &class_ancestry,
9321                );
9322                visitor.parsed.record_materialization(
9323                    MaterializationRecord::RecoveredDeclaration {
9324                        recovery: class_range,
9325                        unit: class_unit.clone(),
9326                    },
9327                );
9328                let member_scope = ScopeInfo {
9329                    package_name: class_scope.package_name.clone(),
9330                    module: class_scope.module.clone(),
9331                    class_unit: Some(class_unit),
9332                    template_signature: class_scope.template_signature.clone(),
9333                    template_metadata: None,
9334                    declarations_are_fields: true,
9335                    recovered_specialization_member_scope: false,
9336                    visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9337                    local_block: None,
9338                    visible_using_declarations: class_scope.visible_using_declarations.clone(),
9339                };
9340                // The padded body reparse is its own tree, so it indexes itself.
9341                let body_root = body_tree.root_node();
9342                visitor.run_container_work(
9343                    body_root,
9344                    member_scope,
9345                    &visitor.recovery_ancestry(body_root),
9346                );
9347            });
9348            // Register only after the padded body reparse: its nodes deliberately
9349            // retain offsets inside the consumed region and must be visited first.
9350            self.consumed_fragment_regions
9351                .push((node.start_byte(), class_close_end));
9352            // An ERROR envelope can hold real sibling declarations after the
9353            // recovered class's close (the suffix-reparse boundary in
9354            // `cpp_sentinel_macro_class_region` partitions, it does not
9355            // consume). Walk the envelope's remaining children normally; the
9356            // consumed region above keeps the recovered class from being
9357            // indexed twice.
9358            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9359                stack.push(CppWork::Container(CppContainer {
9360                    node,
9361                    scope: scope.clone(),
9362                }));
9363            }
9364            return true;
9365        }
9366        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9367            return false;
9368        };
9369        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9370            return false;
9371        };
9372        let root = tree.root_node();
9373        if !cpp_reparsed_items_are_indexable(root, self.source) {
9374            return false;
9375        }
9376        let recovery = cpp_recovery_window(self.source, start, end);
9377        // The reparsed region is its own tree, so this walk indexes it itself.
9378        let reparsed_ancestry = self.recovery_ancestry(root);
9379        self.record_recovered_declarations(recovery, node, |visitor| {
9380            visitor.visit_container(
9381                root,
9382                &reparsed_ancestry,
9383                &scope.package_name,
9384                scope.module.clone(),
9385                scope.class_unit.clone(),
9386                scope.template_signature.clone(),
9387                scope.visible_using_namespaces.clone(),
9388                scope.visible_using_declarations.clone(),
9389            );
9390        });
9391        if end > node.end_byte() {
9392            self.consumed_fragment_regions
9393                .push((node.start_byte(), end));
9394        } else if node.kind() == "ERROR" && node.end_byte() > end {
9395            // The sentinel region ended at the first recovered class-like item
9396            // but the ERROR envelope keeps real sibling declarations after it
9397            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
9398            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
9399            // envelope's remaining children normally; the consumed region
9400            // keeps the reparsed prefix from being indexed twice.
9401            self.consumed_fragment_regions
9402                .push((node.start_byte(), end));
9403            stack.push(CppWork::Container(CppContainer {
9404                node,
9405                scope: scope.clone(),
9406            }));
9407        }
9408        true
9409    }
9410
9411    /// Re-own complete class declarations from the structured Abseil
9412    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
9413    /// its direct CST children already prove both namespace components and the
9414    /// class bodies, so the ordinary class/member visitor can retain ownership
9415    /// and exact source ranges without admitting unrelated callable bodies.
9416    fn visit_nested_namespace_sentinel<'tree>(
9417        &mut self,
9418        node: Node<'tree>,
9419        scope: &ScopeInfo,
9420        ancestry: &ParentIndex<'tree>,
9421    ) -> bool {
9422        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9423            return false;
9424        };
9425        let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9426            recovered.function,
9427            &recovered.namespace_components,
9428            self.source,
9429        );
9430
9431        let mut package_name = scope.package_name.clone();
9432        let mut module = scope.module.clone();
9433        for component in recovered.namespace_components {
9434            package_name = if package_name.is_empty() {
9435                component
9436            } else {
9437                format!("{package_name}::{component}")
9438            };
9439            let namespace_module = CodeUnit::new_fq(
9440                self.file.clone(),
9441                CodeUnitType::Module,
9442                "",
9443                package_name.clone(),
9444                cpp_namespace_fq(&package_name),
9445            );
9446            if !self.parsed.contains_declaration(&namespace_module) {
9447                self.add_declaration(
9448                    ancestry,
9449                    namespace_module.clone(),
9450                    recovered.function,
9451                    None,
9452                    None,
9453                );
9454            }
9455            module = Some(namespace_module);
9456        }
9457
9458        let recovered_scope = ScopeInfo {
9459            package_name,
9460            module,
9461            // This scope's package comes from the namespace levels the sentinel
9462            // shape proves, so its owner must come from the same place. C++
9463            // never nests a namespace inside a class, so a surviving
9464            // `class_unit` here is recovery bleed from an earlier malformed
9465            // region, and keeping it would publish declarations whose package
9466            // names one lexical position and whose owner names another -- the
9467            // hybrid identity that trips the package/short boundary assert in
9468            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
9469            // identity-neutral for valid code, where `class_unit` is always
9470            // empty at a namespace.
9471            class_unit: None,
9472            template_signature: scope.template_signature.clone(),
9473            template_metadata: scope.template_metadata.clone(),
9474            declarations_are_fields: false,
9475            recovered_specialization_member_scope: false,
9476            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9477            local_block: None,
9478            visible_using_declarations: scope.visible_using_declarations.clone(),
9479        };
9480        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9481            recovered.function,
9482            recovered.body,
9483            self.source,
9484            ancestry,
9485        ) {
9486            let mut class_scope = recovered_scope.clone();
9487            if let Some(template_node) = fragmented.template_node {
9488                class_scope.template_signature =
9489                    cpp_template_signature(template_node, fragmented.class_node, self.source);
9490                class_scope.template_metadata = cpp_template_metadata(
9491                    template_node,
9492                    fragmented.class_node,
9493                    self.source,
9494                    ancestry,
9495                );
9496            }
9497            if let Some(outcome) = self
9498                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9499            {
9500                let mut class_stack = Vec::new();
9501                let class_unit = self.visit_named_class_like_shape(
9502                    fragmented.class_node,
9503                    fragmented.name.clone(),
9504                    None,
9505                    true,
9506                    Some(fragmented.fragmented.class_range),
9507                    fragmented.raw_supertypes.clone(),
9508                    Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9509                    &class_scope,
9510                    &mut class_stack,
9511                    ancestry,
9512                );
9513                self.parsed
9514                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
9515                        recovery: fragmented.fragmented.class_range,
9516                        unit: class_unit.clone(),
9517                    });
9518                let mut class_lexical_scope = sentinel_namespace.clone();
9519                class_lexical_scope.push(fragmented.name.clone());
9520                self.visit_sentinel_class_aliases(
9521                    cpp_sentinel_fragmented_class_aliases(&fragmented),
9522                    &class_unit,
9523                    &class_scope,
9524                    &class_lexical_scope,
9525                    ancestry,
9526                );
9527                if self.visit_fragmented_export_class_members(
9528                    node,
9529                    outcome,
9530                    class_unit,
9531                    &class_scope,
9532                ) {
9533                    self.consumed_fragment_regions.push((
9534                        fragmented.consumed_start,
9535                        fragmented.fragmented.class_range.end_byte,
9536                    ));
9537                }
9538            }
9539        }
9540        // The class requirement above is the admission gate; once admitted,
9541        // traverse the whole proven inner namespace body so sibling aliases,
9542        // functions, and variables are not silently discarded. The body is a
9543        // node of the tree being walked, so it reuses that tree's index.
9544        self.run_container_work(recovered.body, recovered_scope, ancestry);
9545        true
9546    }
9547
9548    /// Publish the original structured aliases of a recovered sentinel class
9549    /// as its members, then claim their byte ranges. The claim keeps the member
9550    /// reparse from publishing its statement-context misreading of the same
9551    /// text, and keeps the outer walk from publishing the alias again at
9552    /// namespace scope when the reparse leaves the class region unclaimed.
9553    ///
9554    /// The malformed CST ancestry names only the namespaces outside the
9555    /// sentinel, so the target type is read in `lexical_scope`, the recovered
9556    /// class scope the usage extractor also assigns to these nodes.
9557    fn visit_sentinel_class_aliases<'tree>(
9558        &mut self,
9559        aliases: Vec<Node<'tree>>,
9560        class_unit: &CodeUnit,
9561        scope: &ScopeInfo,
9562        lexical_scope: &[String],
9563        ancestry: &ParentIndex<'tree>,
9564    ) {
9565        let member_scope = ScopeInfo {
9566            package_name: class_unit.package_name().to_string(),
9567            module: scope.module.clone(),
9568            class_unit: Some(class_unit.clone()),
9569            template_signature: scope.template_signature.clone(),
9570            template_metadata: None,
9571            declarations_are_fields: true,
9572            recovered_specialization_member_scope: false,
9573            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9574            local_block: None,
9575            visible_using_declarations: scope.visible_using_declarations.clone(),
9576        };
9577        for alias in aliases {
9578            let mut alias_scope = member_scope.clone();
9579            let declaration = if alias.kind() == "template_declaration" {
9580                let declaration = alias
9581                    .named_children(&mut alias.walk())
9582                    .find(|child| child.kind() == "alias_declaration")
9583                    .expect("an admitted template alias holds its alias declaration");
9584                alias_scope.template_signature =
9585                    cpp_template_signature(alias, declaration, self.source);
9586                alias_scope.template_metadata =
9587                    cpp_template_metadata(alias, declaration, self.source, ancestry);
9588                declaration
9589            } else {
9590                alias
9591            };
9592            self.consumed_fragment_regions
9593                .push((alias.start_byte(), alias.end_byte()));
9594            let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9595                continue;
9596            };
9597            let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9598            let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9599            self.record_type_aliases(
9600                ancestry,
9601                declaration,
9602                &alias_scope,
9603                vec![name.clone()],
9604                signature,
9605                cpp_declaration_range(declaration),
9606                lexical_scope,
9607            );
9608            // The source fact reads its lexical path from the same malformed
9609            // ancestry; structural alias navigation joins this path.
9610            if let Some(declaration) = self
9611                .source_links
9612                .get(&unit)
9613                .and_then(|declarations| declarations.last())
9614            {
9615                let mut properties = self.source_properties.borrow_mut();
9616                let fact = properties
9617                    .get_mut(declaration)
9618                    .expect("a linked alias has a source fact");
9619                fact.lexical_path = lexical_scope.to_vec();
9620                fact.lexical_path.push(name);
9621            }
9622        }
9623    }
9624
9625    fn visit_declaration<'tree>(
9626        &mut self,
9627        node: Node<'tree>,
9628        scope: &ScopeInfo,
9629        in_class_body: bool,
9630        stack: &mut Vec<CppWork<'tree>>,
9631        ancestry: &ParentIndex<'tree>,
9632    ) {
9633        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9634            return;
9635        }
9636        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9637            return;
9638        }
9639        // Statement-context parsing treats Ctor(int value) as a parenthesized
9640        // variable declarator. Validate it as a class member before publishing
9641        // its parameter name as a field.
9642        if in_class_body
9643            && scope.class_unit.as_ref().is_some_and(|owner| {
9644                node.child_by_field_name("type").is_some_and(|ty| {
9645                    ty.kind() == "type_identifier"
9646                        && node_text(ty, self.source) == owner.identifier()
9647                })
9648            })
9649            && node
9650                .child_by_field_name("declarator")
9651                .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9652            && self.visit_bare_constructor_expression(node, scope, ancestry)
9653        {
9654            return;
9655        }
9656        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9657            !cpp_active_template_type_parameter(
9658                node,
9659                node_text(declarator, self.source),
9660                self.source,
9661                ancestry,
9662            )
9663        }) {
9664            return;
9665        }
9666        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9667            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
9668            // the following `Imaging image` member into one malformed field.
9669            // The ERROR's identifier is the actual declarator; route it
9670            // through the ordinary field path so its parent, range, and
9671            // signature metadata remain consistent with every other member.
9672            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9673            return;
9674        }
9675        if in_class_body
9676            && let Some(parent) = scope.class_unit.as_ref()
9677            && let Some(call) =
9678                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9679        {
9680            self.visit_recovered_macro_qualified_constructor_definition(
9681                node, call, scope, ancestry,
9682            );
9683            return;
9684        }
9685        if in_class_body
9686            && let Some(parent) = scope.class_unit.as_ref()
9687            && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9688                node,
9689                parent.identifier(),
9690                self.source,
9691            )
9692        {
9693            self.visit_recovered_macro_initialized_constructor_definition(
9694                node,
9695                constructor,
9696                scope,
9697                ancestry,
9698            );
9699            return;
9700        }
9701        if in_class_body
9702            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9703        {
9704            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9705            return;
9706        }
9707        if in_class_body
9708            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9709        {
9710            for member in members {
9711                self.add_macro_wrapped_declaration(member, scope, ancestry);
9712            }
9713            return;
9714        }
9715        if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9716            return;
9717        }
9718        if in_class_body
9719            && let Some(declarators) =
9720                recovered_macro_qualified_field_declarators(node, self.source)
9721        {
9722            for declarator in declarators {
9723                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9724            }
9725            return;
9726        }
9727        let recovered_alias_names = recovered_type_alias_names(node, self.source);
9728        if !recovered_alias_names.is_empty() {
9729            self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9730            return;
9731        }
9732        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9733            return;
9734        }
9735        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9736            return;
9737        }
9738
9739        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9740            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9741                // Issue #938: the members tree-sitter scattered out of the fragmented
9742                // multiple-base export node are reparsed from their true body region
9743                // and re-owned as members of the recovered class, with an explicit
9744                // navigation range spanning to the displaced closing brace.
9745                if let Some(outcome) =
9746                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9747                {
9748                    let consumed_region = (
9749                        recovered.declaration_node.end_byte(),
9750                        fragmented.class_range.end_byte,
9751                    );
9752                    let code_unit = self.visit_named_class_like_shape(
9753                        recovered.declaration_node,
9754                        recovered.name,
9755                        None,
9756                        true,
9757                        Some(fragmented.class_range),
9758                        recovered.raw_supertypes,
9759                        recovered.base_facts,
9760                        scope,
9761                        stack,
9762                        ancestry,
9763                    );
9764                    self.parsed.record_materialization(
9765                        MaterializationRecord::RecoveredDeclaration {
9766                            recovery: fragmented.class_range,
9767                            unit: code_unit.clone(),
9768                        },
9769                    );
9770                    let consume_fragment =
9771                        self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9772                    // Everything between the fragmented declaration and its displaced
9773                    // closing brace now belongs to the recovered class; keep the
9774                    // ordinary walk from re-indexing those scattered siblings at top
9775                    // level. Register the consumed region only after indexing because
9776                    // the reparsed nodes retain byte offsets inside that same region.
9777                    if consume_fragment {
9778                        self.consumed_fragment_regions.push(consumed_region);
9779                    }
9780                    return;
9781                }
9782            }
9783            let uses_initializer_body = recovered.uses_initializer_body;
9784            let definition_body_present = recovered.body.is_some();
9785            let class_unit = self.visit_named_class_like_shape(
9786                recovered.declaration_node,
9787                recovered.name,
9788                recovered.body,
9789                definition_body_present,
9790                None,
9791                recovered.raw_supertypes,
9792                recovered.base_facts,
9793                scope,
9794                stack,
9795                ancestry,
9796            );
9797            self.parsed
9798                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9799                    recovery: cpp_declaration_range(node),
9800                    unit: class_unit,
9801                });
9802            if uses_initializer_body {
9803                return;
9804            }
9805        }
9806
9807        // A function-like macro invocation that expands to declarations keeps
9808        // no terminator of its own: the semicolons live in the replacement
9809        // body, which is not part of this tree. Tree-sitter recovers one and
9810        // reads the macro name as the member type and its argument list as a
9811        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
9812        // published a field named `ClosedDetect` beside the real constructor
9813        // and split every reference to the constructor's name between the two
9814        // (#3298). The invented terminator is the whole difference from a real
9815        // member: a member terminates itself, and with a terminator the
9816        // grammar reads `T (name);` as a declaration rather than a field.
9817        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9818            return;
9819        }
9820
9821        let mut handled_function = false;
9822        let mut handled_declarator = false;
9823        let mut cursor = node.walk();
9824        for child in node.named_children(&mut cursor) {
9825            if matches!(
9826                child.kind(),
9827                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9828            ) {
9829                // A named class-like definition remains a declaration even when
9830                // the same statement also declares an object, for example
9831                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
9832                // declaration's type and `kind` as its declarator.  Dropping the
9833                // type here loses both its nested owner and every later lexical
9834                // reference to it.  A body is the structured proof that this is
9835                // a definition rather than an elaborated type use such as
9836                // `class Kind value;`.
9837                if cpp_body_node(child).is_some() {
9838                    self.visit_class_like(child, scope, stack, ancestry);
9839                }
9840                continue;
9841            }
9842        }
9843
9844        let mut cursor = node.walk();
9845        for child in node.children_by_field_name("declarator", &mut cursor) {
9846            if crate::structural::is_recovered_designator_init_declarator(child) {
9847                handled_declarator = true;
9848                continue;
9849            }
9850            if in_class_body
9851                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9852            {
9853                handled_declarator = true;
9854                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9855                continue;
9856            }
9857            if let Some(kind) = classify_declarator(child) {
9858                handled_declarator = true;
9859                match kind {
9860                    DeclaratorKind::Function(function_declarator) => {
9861                        handled_function = true;
9862                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9863                    }
9864                    DeclaratorKind::Variable(variable_declarator) => {
9865                        self.visit_variable_declaration(
9866                            node,
9867                            variable_declarator,
9868                            scope,
9869                            in_class_body,
9870                            ancestry,
9871                        );
9872                    }
9873                }
9874            }
9875        }
9876
9877        if !handled_declarator {
9878            let mut cursor = node.walk();
9879            for child in node.named_children(&mut cursor) {
9880                if crate::structural::is_recovered_designator_init_declarator(child) {
9881                    handled_declarator = true;
9882                    continue;
9883                }
9884                if !is_unfielded_declarator_candidate(child) {
9885                    continue;
9886                }
9887                let Some(kind) = classify_declarator(child) else {
9888                    continue;
9889                };
9890                handled_declarator = true;
9891                match kind {
9892                    DeclaratorKind::Function(function_declarator) => {
9893                        handled_function = true;
9894                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9895                    }
9896                    DeclaratorKind::Variable(variable_declarator) => {
9897                        self.visit_variable_declaration(
9898                            node,
9899                            variable_declarator,
9900                            scope,
9901                            in_class_body,
9902                            ancestry,
9903                        );
9904                    }
9905                }
9906            }
9907        }
9908
9909        if handled_function {
9910            return;
9911        }
9912
9913        if !handled_declarator {
9914            if in_class_body {
9915                self.visit_class_members_from_declaration(node, scope, ancestry);
9916            } else {
9917                self.visit_global_variables_from_declaration(node, scope, ancestry);
9918            }
9919        }
9920    }
9921
9922    /// Preserve the member structure of an anonymous aggregate.
9923    ///
9924    /// An anonymous union with no declarator promotes its fields into the
9925    /// containing aggregate. An anonymous struct/union followed by a named
9926    /// declarator, such as `struct { T *ops; } sock`, declares both the field
9927    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
9928    /// the declarator's structured nested identity so a later `value.sock.ops`
9929    /// chain can traverse it without parsing a type spelling (#2407).
9930    ///
9931    /// An anonymous aggregate declares no tag, so there is nothing for the C
9932    /// dialect to re-scope and nothing for the C++ dialect to nest: both
9933    /// readings of this shape are the same, and both need these members
9934    /// (#3301).
9935    fn anonymous_aggregate_declaration_parts<'tree>(
9936        node: Node<'tree>,
9937    ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9938        let aggregate = node.child_by_field_name("type")?;
9939        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9940            || aggregate.child_by_field_name("name").is_some()
9941        {
9942            return None;
9943        }
9944        let body = cpp_body_node(aggregate)?;
9945        let mut cursor = node.walk();
9946        let declarators = node
9947            .children_by_field_name("declarator", &mut cursor)
9948            .filter_map(|declarator| match classify_declarator(declarator) {
9949                Some(DeclaratorKind::Variable(variable)) => Some(variable),
9950                Some(DeclaratorKind::Function(_)) | None => None,
9951            })
9952            .collect();
9953        Some((aggregate, body, declarators))
9954    }
9955
9956    fn visit_anonymous_aggregate_declaration<'tree>(
9957        &mut self,
9958        node: Node<'tree>,
9959        scope: &ScopeInfo,
9960        in_class_body: bool,
9961        stack: &mut Vec<CppWork<'tree>>,
9962        ancestry: &ParentIndex<'tree>,
9963    ) -> bool {
9964        if !in_class_body || scope.class_unit.is_none() {
9965            return false;
9966        }
9967        let Some((aggregate, body, declarators)) =
9968            Self::anonymous_aggregate_declaration_parts(node)
9969        else {
9970            return false;
9971        };
9972        if declarators.is_empty() {
9973            stack.push(CppWork::Container(CppContainer {
9974                node: body,
9975                scope: scope.clone(),
9976            }));
9977            return true;
9978        }
9979
9980        for declarator in declarators {
9981            let Some(name) = extract_variable_name(declarator, self.source) else {
9982                continue;
9983            };
9984            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9985            // The receiver type's identity is the declarator's name, so its
9986            // range is the field declaration that carries that name token.
9987            // The inner specifier ends at the closing brace, which leaves the
9988            // name the unit is known by outside its own text (#3287).
9989            self.visit_named_class_like_shape(
9990                aggregate,
9991                name,
9992                Some(body),
9993                true,
9994                Some(cpp_declaration_range(node)),
9995                None,
9996                None,
9997                scope,
9998                stack,
9999                ancestry,
10000            );
10001        }
10002        true
10003    }
10004
10005    /// Give a function-local anonymous aggregate a structured owner so its
10006    /// direct pointer bindings can be typed by the usage graph.  Unlike an
10007    /// anonymous aggregate in a class body, there is no source-level tag or
10008    /// typedef to supply an identity. Its rank within the named callable owner
10009    /// supplies a generated identity; no source spelling is consulted.  The declaration's own variable remains a file-level
10010    /// field projection, while the aggregate body is visited as the generated
10011    /// class's field list.
10012    fn visit_anonymous_local_aggregate_declaration<'tree>(
10013        &mut self,
10014        node: Node<'tree>,
10015        scope: &ScopeInfo,
10016        stack: &mut Vec<CppWork<'tree>>,
10017        ancestry: &ParentIndex<'tree>,
10018    ) -> bool {
10019        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
10020            return false;
10021        }
10022        let Some((aggregate, body, declarators)) =
10023            Self::anonymous_aggregate_declaration_parts(node)
10024        else {
10025            return false;
10026        };
10027        if declarators.is_empty() {
10028            return false;
10029        }
10030
10031        for declarator in &declarators {
10032            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
10033        }
10034        let synthetic = cpp_synthetic_name(aggregate);
10035        let name = format!("{}@{}", synthetic.kind, synthetic.rank);
10036        self.visit_named_class_like_shape(
10037            aggregate,
10038            name,
10039            Some(body),
10040            true,
10041            None,
10042            None,
10043            None,
10044            scope,
10045            stack,
10046            ancestry,
10047        );
10048        true
10049    }
10050
10051    fn visit_function_declaration<'tree>(
10052        &mut self,
10053        declaration_node: Node<'tree>,
10054        declarator: Node<'tree>,
10055        scope: &ScopeInfo,
10056        ancestry: &ParentIndex<'tree>,
10057    ) {
10058        let Some(function) = extract_function_info(declarator, self.source, scope) else {
10059            return;
10060        };
10061        // An in-class member prototype is a real source declaration, not a
10062        // generated or recovery-only unit, so it keeps the ordinary code-unit
10063        // identity (#3508). #3493 recorded its callable modifiers, which is
10064        // what a canonical procedure key needs; a synthetic unit would still
10065        // refuse that key on identity grounds alone.
10066        let code_unit = function.code_unit(self.file.clone());
10067        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10068            return;
10069        }
10070        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10071        let signature = render_cpp_function_display_signature_from_node(
10072            declaration_node,
10073            self.source,
10074            scope.template_signature.as_deref(),
10075            false,
10076            ancestry,
10077        );
10078        self.add_signature_with_metadata(
10079            code_unit.clone(),
10080            cpp_signature_metadata(
10081                signature,
10082                declarator,
10083                cpp_callable_linkage(declaration_node, self.source, ancestry),
10084                self.source,
10085                ancestry,
10086            )
10087            .with_declaration_only(true),
10088        );
10089        if let Some(parent) = &scope.class_unit {
10090            self.parsed.add_child(parent.clone(), code_unit);
10091        } else if let Some(module) = &scope.module {
10092            self.parsed.add_child(module.clone(), code_unit);
10093        }
10094    }
10095
10096    fn link_existing_source_declaration(
10097        &mut self,
10098        ancestry: &ParentIndex<'_>,
10099        code_unit: &CodeUnit,
10100        declaration_node: Node<'_>,
10101    ) -> bool {
10102        if !self.parsed.contains_declaration(code_unit) {
10103            return false;
10104        }
10105        self.link_source_declaration(ancestry, code_unit, declaration_node);
10106        self.parsed
10107            .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
10108        true
10109    }
10110
10111    fn visit_recovered_macro_qualified_function_declaration<'tree>(
10112        &mut self,
10113        declaration_node: Node<'tree>,
10114        call: Node<'tree>,
10115        scope: &ScopeInfo,
10116        ancestry: &ParentIndex<'tree>,
10117    ) {
10118        let Some(parent) = &scope.class_unit else {
10119            return;
10120        };
10121        let Some(name_node) = call.child_by_field_name("function") else {
10122            return;
10123        };
10124        let Some(arguments) = call.child_by_field_name("arguments") else {
10125            return;
10126        };
10127        let Some((signature, parameter_labels, discriminator)) =
10128            recovered_macro_qualified_function_parameters(arguments, self.source)
10129        else {
10130            return;
10131        };
10132        let arity = parameter_labels.len();
10133        let function = FunctionInfo {
10134            package_name: scope.package_name.clone(),
10135            owner: Some(CppMemberOwner::Unit(parent.clone())),
10136            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10137            signature,
10138        };
10139        if function.name.is_empty() {
10140            return;
10141        }
10142        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10143        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10144            return;
10145        }
10146        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10147        let signature_label = render_cpp_function_display_signature_from_node(
10148            declaration_node,
10149            self.source,
10150            scope.template_signature.as_deref(),
10151            false,
10152            ancestry,
10153        );
10154        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10155        // Only the callable's parameters came from the reparse: the
10156        // `field_declaration` this recovery reads is a real node in a real
10157        // class body, so its storage class and the body's access ladder state
10158        // this member's modifiers exactly as an ordinary member's do.
10159        let modifiers =
10160            cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10161        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10162            .with_declaration_only(true)
10163            .with_callable_arity(CallableArity::exact(arity))
10164            .with_overload_discriminator(Some(discriminator))
10165            .with_callable_linkage(linkage)
10166            .with_callable_modifiers(
10167                modifiers.is_static,
10168                modifiers.is_constructor,
10169                modifiers.visibility,
10170            );
10171        self.add_signature_with_metadata(code_unit.clone(), metadata);
10172        self.parsed.add_child(parent.clone(), code_unit);
10173    }
10174
10175    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10176        &mut self,
10177        declaration_node: Node<'tree>,
10178        call: Node<'tree>,
10179        scope: &ScopeInfo,
10180        ancestry: &ParentIndex<'tree>,
10181    ) {
10182        let Some(parent) = &scope.class_unit else {
10183            return;
10184        };
10185        let Some(arguments) = call.child_by_field_name("arguments") else {
10186            return;
10187        };
10188        let Some((mut signature, parameter_labels, discriminator)) =
10189            recovered_macro_qualified_function_parameters(arguments, self.source)
10190        else {
10191            return;
10192        };
10193        if let Some(template_signature) = &scope.template_signature {
10194            signature = format!("{template_signature}{signature}");
10195        }
10196        let arity = parameter_labels.len();
10197        let function = FunctionInfo {
10198            package_name: scope.package_name.clone(),
10199            owner: Some(CppMemberOwner::Unit(parent.clone())),
10200            name: parent.identifier().to_string(),
10201            signature,
10202        };
10203        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10204        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10205        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10206        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10207        // The recovery already proved this member is the class's constructor:
10208        // the reparsed call names the enclosing class. Everything else is read
10209        // from the real `field_declaration` the recovery found it in.
10210        let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10211        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10212            .with_declaration_only(false)
10213            .with_callable_arity(CallableArity::exact(arity))
10214            .with_overload_discriminator(Some(discriminator))
10215            .with_callable_linkage(linkage)
10216            .with_callable_modifiers(
10217                modifiers.is_static,
10218                modifiers.is_constructor,
10219                modifiers.visibility,
10220            );
10221        self.add_signature_with_metadata(code_unit.clone(), metadata);
10222        self.parsed.add_child(parent.clone(), code_unit);
10223    }
10224
10225    /// Publish the constructor of the macro-decorated field shape that
10226    /// `recovered_macro_initialized_constructor_declarator` proves. The head
10227    /// is a real, clean function declarator, so it keeps the ordinary
10228    /// identity and metadata; the `default_value` initializer list is the
10229    /// constructor body, so the occurrence is a definition.
10230    fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10231        &mut self,
10232        declaration_node: Node<'tree>,
10233        function_declarator: Node<'tree>,
10234        scope: &ScopeInfo,
10235        ancestry: &ParentIndex<'tree>,
10236    ) {
10237        let Some(parent) = &scope.class_unit else {
10238            return;
10239        };
10240        let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10241            return;
10242        };
10243        let code_unit = function.code_unit(self.file.clone());
10244        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10245        // Render the head the way an ordinary constructor definition renders
10246        // the text ahead of its body, member initializers included.
10247        let body_start = declaration_node
10248            .child_by_field_name("default_value")
10249            .map_or(declaration_node.end_byte(), |body| body.start_byte());
10250        let head =
10251            collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10252        let signature = match scope.template_signature.as_deref() {
10253            Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10254            None => format!("{head} {{...}}"),
10255        };
10256        self.add_signature_with_metadata(
10257            code_unit.clone(),
10258            cpp_signature_metadata(
10259                signature,
10260                function_declarator,
10261                cpp_callable_linkage(declaration_node, self.source, ancestry),
10262                self.source,
10263                ancestry,
10264            )
10265            .with_declaration_only(false),
10266        );
10267        self.parsed.add_child(parent.clone(), code_unit);
10268    }
10269
10270    fn visit_variable_declaration<'tree>(
10271        &mut self,
10272        declaration_node: Node<'tree>,
10273        declarator: Node<'tree>,
10274        scope: &ScopeInfo,
10275        in_class_body: bool,
10276        ancestry: &ParentIndex<'tree>,
10277    ) {
10278        let Some(name) = extract_variable_name(declarator, self.source) else {
10279            return;
10280        };
10281        let parent = if in_class_body {
10282            let Some(parent) = &scope.class_unit else {
10283                return;
10284            };
10285            Some(parent)
10286        } else {
10287            None
10288        };
10289        let short_name = match parent {
10290            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10291            None => name.clone(),
10292        };
10293        let fq = cpp_leaf_fq(
10294            &scope.package_name,
10295            parent,
10296            &name,
10297            SegmentKind::Member,
10298            SegmentKind::Member,
10299        );
10300        let code_unit = CodeUnit::new_fq(
10301            self.file.clone(),
10302            CodeUnitType::Field,
10303            scope.package_name.clone(),
10304            short_name,
10305            fq,
10306        );
10307        if self.parsed.contains_declaration(&code_unit) {
10308            return;
10309        }
10310        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10311        self.add_signature_with_metadata(
10312            code_unit.clone(),
10313            SignatureMetadata::new(
10314                render_cpp_field_signature(declaration_node, declarator, self.source),
10315                Vec::new(),
10316            )
10317            .with_cpp_field_linkage(cpp_field_declaration_linkage(
10318                declaration_node,
10319                self.source,
10320                ancestry,
10321            )),
10322        );
10323        if let Some(parent) = &scope.class_unit {
10324            self.parsed.add_child(parent.clone(), code_unit);
10325        } else if let Some(module) = &scope.module {
10326            self.parsed.add_child(module.clone(), code_unit);
10327        }
10328    }
10329
10330    /// Index the field half of an omitted-semicolon pair the macro-declaration
10331    /// grammar folded into the following function definition's head. The
10332    /// function itself continues through the ordinary visitor path.
10333    fn visit_omitted_semicolon_macro_field<'tree>(
10334        &mut self,
10335        node: Node<'tree>,
10336        type_modifier: Node<'tree>,
10337        name_modifier: Node<'tree>,
10338        scope: &ScopeInfo,
10339        ancestry: &ParentIndex<'tree>,
10340    ) {
10341        let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10342            return;
10343        };
10344        let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10345        if name.is_empty() {
10346            return;
10347        }
10348        let range = Range {
10349            start_byte: type_modifier.start_byte(),
10350            end_byte: name_modifier.end_byte(),
10351            start_line: type_modifier.start_position().row + 1,
10352            end_line: name_modifier.end_position().row + 1,
10353        };
10354        self.record_macro_head_field(node, range, name, scope, ancestry);
10355    }
10356
10357    /// Index the macro-return stand-in field: a clean function definition whose
10358    /// macro-modified head returns an active template type parameter. The field
10359    /// is named after the return type and spans the head up to it, exactly the
10360    /// phantom `field_declaration` the older grammar emitted (#1129).
10361    fn visit_macro_return_template_parameter_field<'tree>(
10362        &mut self,
10363        node: Node<'tree>,
10364        head_start: Node<'tree>,
10365        type_node: Node<'tree>,
10366        scope: &ScopeInfo,
10367        ancestry: &ParentIndex<'tree>,
10368    ) {
10369        let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10370        let range = Range {
10371            start_byte: head_start.start_byte(),
10372            end_byte: type_node.end_byte(),
10373            start_line: head_start.start_position().row + 1,
10374            end_line: type_node.end_position().row + 1,
10375        };
10376        self.record_macro_head_field(node, range, name, scope, ancestry);
10377    }
10378
10379    /// Record one field recovered from a function definition's macro head. The
10380    /// signature is the head's own text: like the older grammar's phantom
10381    /// field_declaration, it carries no semicolon because none was written.
10382    fn record_macro_head_field<'tree>(
10383        &mut self,
10384        node: Node<'tree>,
10385        range: Range,
10386        name: String,
10387        scope: &ScopeInfo,
10388        ancestry: &ParentIndex<'tree>,
10389    ) {
10390        if name.is_empty() {
10391            return;
10392        }
10393        let parent = scope.class_unit.as_ref();
10394        let short_name = match parent {
10395            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10396            None => name.clone(),
10397        };
10398        let code_unit = CodeUnit::new_fq(
10399            self.file.clone(),
10400            CodeUnitType::Field,
10401            scope.package_name.clone(),
10402            short_name,
10403            cpp_leaf_fq(
10404                &scope.package_name,
10405                parent,
10406                &name,
10407                SegmentKind::Member,
10408                SegmentKind::Member,
10409            ),
10410        );
10411        if self.parsed.contains_declaration(&code_unit) {
10412            return;
10413        }
10414        self.add_declaration_with_range(
10415            ancestry,
10416            code_unit.clone(),
10417            range,
10418            Some(node),
10419            parent.cloned(),
10420            None,
10421        );
10422        let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10423        self.add_signature_with_metadata(
10424            code_unit.clone(),
10425            SignatureMetadata::new(signature, Vec::new())
10426                .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10427        );
10428        if let Some(parent) = &scope.class_unit {
10429            self.parsed.add_child(parent.clone(), code_unit);
10430        } else if let Some(module) = &scope.module {
10431            self.parsed.add_child(module.clone(), code_unit);
10432        }
10433    }
10434
10435    fn variable_declarator_from_declaration_child<'tree>(
10436        child: Node<'tree>,
10437    ) -> Option<Node<'tree>> {
10438        if child.kind() == "init_declarator" {
10439            child.child_by_field_name("declarator")
10440        } else if matches!(
10441            child.kind(),
10442            "identifier"
10443                | "field_identifier"
10444                | "pointer_declarator"
10445                | "reference_declarator"
10446                | "array_declarator"
10447                | "parenthesized_declarator"
10448                | "macro_modifier_declarator"
10449        ) {
10450            Some(child)
10451        } else {
10452            None
10453        }
10454    }
10455
10456    fn visit_class_members_from_declaration<'tree>(
10457        &mut self,
10458        node: Node<'tree>,
10459        scope: &ScopeInfo,
10460        ancestry: &ParentIndex<'tree>,
10461    ) {
10462        let mut cursor = node.walk();
10463        for child in node.named_children(&mut cursor) {
10464            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10465                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10466            } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10467            {
10468                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10469            }
10470        }
10471    }
10472
10473    fn visit_global_variables_from_declaration<'tree>(
10474        &mut self,
10475        node: Node<'tree>,
10476        scope: &ScopeInfo,
10477        ancestry: &ParentIndex<'tree>,
10478    ) {
10479        let mut cursor = node.walk();
10480        for child in node.named_children(&mut cursor) {
10481            if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10482                self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10483            }
10484        }
10485    }
10486
10487    fn visit_type_declaration<'tree>(
10488        &mut self,
10489        node: Node<'tree>,
10490        scope: &ScopeInfo,
10491        stack: &mut Vec<CppWork<'tree>>,
10492        ancestry: &ParentIndex<'tree>,
10493    ) {
10494        let type_node = node.child_by_field_name("type");
10495        if let Some(type_node) = type_node
10496            && matches!(
10497                type_node.kind(),
10498                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10499            )
10500        {
10501            self.visit_class_like(type_node, scope, stack, ancestry);
10502        }
10503
10504        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10505            let range = Range {
10506                start_byte: node.start_byte(),
10507                end_byte: recovered.end_node.end_byte(),
10508                start_line: node.start_position().row + 1,
10509                end_line: recovered.end_node.end_position().row + 1,
10510            };
10511            let signature = self
10512                .source
10513                .get(range.start_byte..range.end_byte)
10514                .map(normalize_cpp_whitespace)
10515                .unwrap_or_default();
10516            self.record_type_aliases(
10517                ancestry,
10518                node,
10519                scope,
10520                vec![recovered.name],
10521                signature,
10522                range,
10523                &cpp_callable_lexical_scope(node, self.source, ancestry),
10524            );
10525            return;
10526        }
10527
10528        let alias_names = match node.kind() {
10529            "alias_declaration" => extract_alias_declaration_name(node, self.source)
10530                .into_iter()
10531                .collect::<Vec<_>>(),
10532            "type_definition" => extract_typedef_alias_names(node, self.source),
10533            _ => Vec::new(),
10534        };
10535        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10536            (type_node, alias_names.as_slice())
10537            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10538            && type_node.child_by_field_name("name").is_none()
10539        {
10540            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10541        } else {
10542            None
10543        };
10544        self.add_type_aliases(ancestry, node, scope, alias_names);
10545        if let Some((body, alias_name)) = anonymous_aggregate {
10546            // The typedef alias is also the only user-visible identity of an
10547            // anonymous aggregate. Reuse it as the member owner instead of
10548            // minting a second signatureless class with the same FQN. The
10549            // latter makes forward lookup ambiguous when conditional aliases
10550            // coexist and returns duplicate definitions even without guards.
10551            let signature = normalize_cpp_whitespace(node_text(node, self.source));
10552            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10553            debug_assert!(self.parsed.contains_declaration(&alias_unit));
10554            let mut nested_scope = scope.clone();
10555            nested_scope.class_unit = Some(alias_unit);
10556            nested_scope.template_signature = scope.template_signature.clone();
10557            nested_scope.template_metadata = None;
10558            nested_scope.declarations_are_fields = false;
10559            nested_scope.recovered_specialization_member_scope = false;
10560            stack.push(CppWork::Container(CppContainer {
10561                node: body,
10562                scope: nested_scope,
10563            }));
10564        }
10565    }
10566
10567    fn add_type_aliases(
10568        &mut self,
10569        ancestry: &ParentIndex<'_>,
10570        node: Node<'_>,
10571        scope: &ScopeInfo,
10572        alias_names: Vec<String>,
10573    ) {
10574        let signature = normalize_cpp_whitespace(node_text(node, self.source));
10575        self.record_type_aliases(
10576            ancestry,
10577            node,
10578            scope,
10579            alias_names,
10580            signature,
10581            cpp_declaration_range(node),
10582            &cpp_callable_lexical_scope(node, self.source, ancestry),
10583        );
10584    }
10585
10586    /// `lexical_scope` is where the alias target's names are looked up: the
10587    /// alias's CST ancestry, or the owner a structural recovery proved when
10588    /// the CST ancestry is malformed.
10589    #[allow(clippy::too_many_arguments)]
10590    fn record_type_aliases(
10591        &mut self,
10592        ancestry: &ParentIndex<'_>,
10593        node: Node<'_>,
10594        scope: &ScopeInfo,
10595        alias_names: Vec<String>,
10596        signature: String,
10597        range: Range,
10598        lexical_scope: &[String],
10599    ) {
10600        if signature.is_empty() {
10601            return;
10602        }
10603        let type_name = node
10604            .child_by_field_name("type")
10605            .and_then(|type_node| type_node.child_by_field_name("name"))
10606            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10607        for alias_name in alias_names {
10608            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10609                continue;
10610            }
10611            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10612            // Declaration identity does not include the alias signature. Keep
10613            // each physical range so conditional aliases retain their guards.
10614            self.note_declaration(&code_unit);
10615            self.link_source_declaration(ancestry, &code_unit, node);
10616            self.parsed
10617                .add_code_unit_with_range(code_unit.clone(), range, None, None);
10618            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10619                cpp_structured_type_identity(type_node, self.source, lexical_scope)
10620            });
10621            self.add_signature_with_metadata(
10622                code_unit.clone(),
10623                SignatureMetadata::new(signature.clone(), Vec::new())
10624                    .with_underlying_type_identity(underlying_type_identity),
10625            );
10626            if let Some(metadata) = &scope.template_metadata {
10627                let mut metadata = metadata.clone();
10628                metadata.primary_fq_name = code_unit.fq_name();
10629                self.parsed
10630                    .set_cpp_template_metadata(code_unit.clone(), metadata);
10631            }
10632            if let Some(parent) = &scope.class_unit {
10633                self.parsed.add_child(parent.clone(), code_unit.clone());
10634            } else if let Some(module) = &scope.module {
10635                self.parsed.add_child(module.clone(), code_unit.clone());
10636            }
10637            self.parsed.mark_type_alias(code_unit);
10638        }
10639    }
10640
10641    fn type_alias_unit(
10642        &self,
10643        scope: &ScopeInfo,
10644        alias_name: String,
10645        signature: String,
10646    ) -> CodeUnit {
10647        let short_name = if let Some(parent) = &scope.class_unit {
10648            cpp_join_nested_short(parent.short_name(), &alias_name)
10649        } else {
10650            alias_name.clone()
10651        };
10652        let fq = cpp_leaf_fq(
10653            &scope.package_name,
10654            scope.class_unit.as_ref(),
10655            &alias_name,
10656            SegmentKind::Nested,
10657            SegmentKind::Type,
10658        );
10659        CodeUnit::with_signature_and_fq(
10660            self.file.clone(),
10661            CodeUnitType::Class,
10662            scope.package_name.clone(),
10663            short_name,
10664            Some(signature),
10665            false,
10666            fq,
10667        )
10668    }
10669
10670    fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10671        // A comment can terminate tree-sitter's preproc_arg before the logical
10672        // directive ends. Its remaining declarations are replacement locals,
10673        // not file-scope fields, even when recovery exposes them as siblings.
10674        if let Some(replacement) =
10675            crate::graph::syntax::function_macro_replacement_span(node, self.source)
10676        {
10677            self.consumed_fragment_regions
10678                .push((replacement.start, replacement.end));
10679        }
10680        let Some(name) = extract_macro_name(node, self.source) else {
10681            return;
10682        };
10683        let signature = node_text(node, self.source).trim_end().to_string();
10684        if signature.is_empty() {
10685            return;
10686        }
10687        let fq = cpp_member_fq("", &name);
10688        // A macro can be undefined and redefined later in the same file. Its
10689        // structured directive is part of the declaration identity so the
10690        // temporal environment can navigate to the definition active at a
10691        // reference instead of collapsing every spelling to the first range.
10692        // The same physical directive parsed through another C/C++ reading
10693        // still produces the same unit and remains deduplicated.
10694        let code_unit = CodeUnit::with_signature_and_fq(
10695            self.file.clone(),
10696            CodeUnitType::Macro,
10697            "",
10698            name.clone(),
10699            Some(signature.clone()),
10700            false,
10701            fq,
10702        );
10703        if !self.parsed.contains_declaration(&code_unit) {
10704            self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10705            let name_range = node
10706                .child_by_field_name("name")
10707                .map(cpp_declaration_range)
10708                .unwrap_or_else(|| cpp_declaration_range(node));
10709            self.parsed
10710                .record_materialization(MaterializationRecord::GeneratedDeclaration {
10711                    site: cpp_declaration_range(node),
10712                    argument: name_range,
10713                    kind: GenerationKind::PreprocessorDefinition,
10714                    unit: code_unit.clone(),
10715                });
10716            self.parsed.add_signature(code_unit, signature);
10717        }
10718        if node.kind() == "preproc_def" {
10719            update_object_macro_field_environment(
10720                node,
10721                self.source,
10722                &mut self.object_macro_fields,
10723                &mut self.ambiguous_object_macro_fields,
10724            );
10725        } else {
10726            self.object_macro_fields.remove(&name);
10727            self.ambiguous_object_macro_fields.remove(&name);
10728        }
10729    }
10730
10731    /// Bare object-like field-list macros inside an otherwise well-formed
10732    /// aggregate remain identifier nodes in a field declaration. More than
10733    /// one adjacent invocation can be folded into the same declaration, so
10734    /// inspect all of its structured identifier nodes.
10735    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10736        if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10737            return false;
10738        }
10739        // Claiming the declaration means its members are accounted for, which
10740        // is only true when there is an owner to materialize them into. A file
10741        // scope reaches this with a declaration that merely *contains* an
10742        // aggregate spelling an invocation (tree-sitter wraps libuv's
10743        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
10744        // `field_declaration` under a malformed header), and consuming it there
10745        // would discard the aggregate itself (#2985).
10746        if scope.class_unit.is_none() {
10747            return false;
10748        }
10749        let macro_nodes =
10750            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10751        for macro_node in &macro_nodes {
10752            let name = node_text(*macro_node, self.source).trim();
10753            let fields = object_macro_field_closure(&self.object_macro_fields, name);
10754            self.materialize_object_macro_fields(
10755                fields,
10756                cpp_declaration_range(*macro_node),
10757                *macro_node,
10758                scope,
10759            );
10760        }
10761        let Some(last) = macro_nodes.last() else {
10762            return false;
10763        };
10764        // Claiming the declaration accounts for every member it spells, not
10765        // only the donated ones: an invocation folded together with the
10766        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
10767        // leaves those members shapeless, and the grammar recovers them from
10768        // the byte range that follows the invocation.
10769        self.record_collapsed_aggregate_fields(
10770            last.end_byte()..node.end_byte(),
10771            node.start_position().row
10772                + 1
10773                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10774            node,
10775            scope,
10776        );
10777        true
10778    }
10779
10780    /// Retain the grammar's field interpretation with the original invocation
10781    /// or aggregate context. The replacement AST owns the type, while only the
10782    /// original source owns activation, guards, and occurrence ranges.
10783    fn capture_recovered_field_facts(
10784        &mut self,
10785        unit: &CodeUnit,
10786        range: Range,
10787        context: Node<'_>,
10788        field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10789        names_function_type: bool,
10790    ) {
10791        let declaration = *self.source_links[unit]
10792            .last()
10793            .expect("recovered field follows its source occurrence");
10794        let mut fact = crate::source_facts::capture_declaration(
10795            declaration,
10796            context,
10797            unit,
10798            self.source,
10799            self.primary_ancestry,
10800        );
10801        fact.field_type = field_type;
10802        fact.names_function_type = names_function_type;
10803        fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10804        let captured = crate::source_context::capture_declaration_context(
10805            context,
10806            unit,
10807            self.source,
10808            &mut self.preprocessor_context_cache,
10809        );
10810        fact.guard_requirements = captured.guard_requirements;
10811        fact.callable_guards = fact.guard_requirements.clone();
10812        fact.callable_activation = Some(range.end_byte);
10813        fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10814        fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10815        let intern = |node: Node<'_>| {
10816            let mut collector = self.source_collector.borrow_mut();
10817            if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10818                collector.intern_node(node)
10819            } else {
10820                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10821            }
10822        };
10823        fact.conditional_family =
10824            crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10825        fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10826        fact.displaced_namespace_closing_brace =
10827            captured.displaced_namespace_closing_brace.map(intern);
10828        self.capture_source_dependencies(&mut fact, context, unit);
10829        self.apply_active_source_context(&mut fact);
10830        assert!(
10831            self.source_properties
10832                .borrow_mut()
10833                .insert(declaration, fact)
10834                .is_none()
10835        );
10836    }
10837
10838    fn materialize_object_macro_fields(
10839        &mut self,
10840        fields: Vec<MacroReplacementField>,
10841        range: Range,
10842        context: Node<'_>,
10843        scope: &ScopeInfo,
10844    ) {
10845        let Some(owner) = scope.class_unit.as_ref() else {
10846            return;
10847        };
10848        for field in fields {
10849            let signature = field.declaration.clone();
10850            let mut fq = owner.fq().clone();
10851            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10852            let short_name = if owner.short_name().is_empty() {
10853                field.name.clone()
10854            } else {
10855                format!("{}.{}", owner.short_name(), field.name)
10856            };
10857            let code_unit = CodeUnit::with_signature_and_fq(
10858                self.file.clone(),
10859                CodeUnitType::Field,
10860                owner.package_name().to_string(),
10861                short_name,
10862                Some(field.declaration),
10863                true,
10864                fq,
10865            );
10866            if self.parsed.contains_declaration(&code_unit) {
10867                continue;
10868            }
10869            self.add_declaration_with_range(
10870                self.primary_ancestry,
10871                code_unit.clone(),
10872                range,
10873                None,
10874                Some(owner.clone()),
10875                None,
10876            );
10877            self.capture_recovered_field_facts(
10878                &code_unit,
10879                range,
10880                context,
10881                field.field_type,
10882                field.names_function_type,
10883            );
10884            self.parsed.add_signature(code_unit, signature);
10885        }
10886    }
10887
10888    fn collapsed_aggregate_owner(
10889        &self,
10890        name: &str,
10891        scope: &ScopeInfo,
10892        parent: Option<&CodeUnit>,
10893    ) -> CodeUnit {
10894        let short_name = parent.map_or_else(
10895            || name.to_string(),
10896            |parent| cpp_join_nested_short(parent.short_name(), name),
10897        );
10898        let fq = cpp_leaf_fq(
10899            &scope.package_name,
10900            parent,
10901            name,
10902            SegmentKind::Nested,
10903            SegmentKind::Type,
10904        );
10905        CodeUnit::with_signature_and_fq(
10906            self.file.clone(),
10907            CodeUnitType::Class,
10908            scope.package_name.clone(),
10909            short_name,
10910            None,
10911            false,
10912            fq,
10913        )
10914    }
10915
10916    /// Recover ordinary aggregates whose field-list macro invocations made
10917    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
10918    /// The class markers, aggregate braces, field nodes, and macro identifier
10919    /// nodes are all read from the AST; no source delimiter or token scan is
10920    /// used. A close-brace node inside a malformed field closes the innermost
10921    /// recovered aggregate, preserving nested-owner identity.
10922    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10923        let mut cursor = node.walk();
10924        let children = node.children(&mut cursor).collect::<Vec<_>>();
10925        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10926        let mut object_macro_fields = self.object_macro_fields.clone();
10927        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10928        let mut open = Vec::<usize>::new();
10929        let mut index = 0;
10930        while index < children.len() {
10931            let keyword = children[index];
10932            if update_object_macro_field_environment(
10933                keyword,
10934                self.source,
10935                &mut object_macro_fields,
10936                &mut ambiguous_object_macro_fields,
10937            ) {
10938                index += 1;
10939                continue;
10940            }
10941            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10942                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10943                if !name.is_empty() {
10944                    let parent = open
10945                        .last()
10946                        .and_then(|class| recovered.get(*class))
10947                        .map(|(owner, _, _, _)| owner.clone())
10948                        .or_else(|| scope.class_unit.clone());
10949                    let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10950                    recovered.push((
10951                        owner,
10952                        head.key.start_byte(),
10953                        head.opening.end_byte(),
10954                        Vec::new(),
10955                    ));
10956                    let class_index = recovered.len() - 1;
10957                    match head.folded_members {
10958                        // The ordinary shape leaves the aggregate open: every
10959                        // child that follows belongs to it until a close brace
10960                        // ends it.
10961                        None => open.push(class_index),
10962                        // A folded head carries its own member list and its own
10963                        // closing brace, so it closes here. The invocations that
10964                        // precede the class key are the dangling tail of the
10965                        // replacement that folded it, and belong to whatever
10966                        // aggregate is open around it, not to this one.
10967                        Some(members) => {
10968                            let macro_nodes = object_macro_identifier_nodes_with_environment(
10969                                children[index],
10970                                self.source,
10971                                &mut object_macro_fields,
10972                                &mut ambiguous_object_macro_fields,
10973                            );
10974                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10975                                .iter()
10976                                .partition(|node| node.start_byte() < head.key.start_byte());
10977                            if let Some(&enclosing) = open.last() {
10978                                for macro_node in preceding {
10979                                    recovered[enclosing]
10980                                        .3
10981                                        .push(CppCollapsedMember::MacroFields {
10982                                            context: macro_node,
10983                                            range: cpp_declaration_range(macro_node),
10984                                            fields: object_macro_field_closure(
10985                                                &object_macro_fields,
10986                                                &normalize_cpp_whitespace(node_text(
10987                                                    macro_node,
10988                                                    self.source,
10989                                                )),
10990                                            ),
10991                                        });
10992                                }
10993                            }
10994                            let closing = cpp_collapsed_aggregate_closing_brace(members);
10995                            recovered[class_index]
10996                                .3
10997                                .extend(cpp_collapsed_aggregate_members(
10998                                    members,
10999                                    &inner,
11000                                    head.opening.end_byte()..closing,
11001                                    head.opening.end_position().row + 1,
11002                                    self.source,
11003                                    &object_macro_fields,
11004                                ));
11005                            recovered[class_index].2 = members.end_byte();
11006                            for &open_class in &open {
11007                                recovered[open_class].2 =
11008                                    recovered[open_class].2.max(members.end_byte());
11009                            }
11010                        }
11011                    }
11012                    index += head.width;
11013                    continue;
11014                }
11015            }
11016            if let Some(&class_index) = open.last()
11017                && children[index].kind() == "field_declaration"
11018            {
11019                let field = children[index];
11020                let macro_nodes = object_macro_identifier_nodes_with_environment(
11021                    field,
11022                    self.source,
11023                    &mut object_macro_fields,
11024                    &mut ambiguous_object_macro_fields,
11025                );
11026                recovered[class_index]
11027                    .3
11028                    .extend(cpp_collapsed_aggregate_members(
11029                        field,
11030                        &macro_nodes,
11031                        field.start_byte()..field.end_byte(),
11032                        field.start_position().row + 1,
11033                        self.source,
11034                        &object_macro_fields,
11035                    ));
11036                let end = field.end_byte();
11037                for &open_class in &open {
11038                    recovered[open_class].2 = recovered[open_class].2.max(end);
11039                }
11040                let closes = count_close_brace_nodes(field);
11041                for _ in 0..closes {
11042                    if let Some(closed) = open.pop() {
11043                        recovered[closed].2 = end;
11044                    }
11045                }
11046            }
11047            index += 1;
11048        }
11049
11050        let mut owners = Vec::with_capacity(recovered.len());
11051        for (owner, start, end, members) in recovered {
11052            let parent = owners
11053                .iter()
11054                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
11055                .cloned()
11056                .or_else(|| scope.class_unit.clone());
11057            self.declare_collapsed_aggregate(
11058                owner.clone(),
11059                start..end,
11060                Some(node),
11061                parent,
11062                members,
11063                scope,
11064            );
11065            owners.push(owner);
11066        }
11067    }
11068
11069    /// Record one recovered aggregate and the members it declares.
11070    fn declare_collapsed_aggregate(
11071        &mut self,
11072        owner: CodeUnit,
11073        span: std::ops::Range<usize>,
11074        evidence: Option<Node<'_>>,
11075        parent: Option<CodeUnit>,
11076        members: Vec<CppCollapsedMember<'_>>,
11077        scope: &ScopeInfo,
11078    ) {
11079        let range = Range {
11080            start_byte: span.start,
11081            end_byte: span.end,
11082            start_line: self.source.get(..span.start).map_or(1, |source| {
11083                source.bytes().filter(|byte| *byte == b'\n').count() + 1
11084            }),
11085            end_line: self.source.get(..span.end).map_or(1, |source| {
11086                source.bytes().filter(|byte| *byte == b'\n').count() + 1
11087            }),
11088        };
11089        // The tag can already be declared by a typedef or a forward
11090        // declaration, and this range is the definition's. Record it either
11091        // way: a declaration carries every range it is written at, and
11092        // dropping this one leaves nothing able to prove that the aggregate
11093        // has a body (#3098). Recording a range that is already held, or a
11094        // unit that is already declared, changes nothing.
11095        self.add_declaration_with_range(
11096            self.primary_ancestry,
11097            owner.clone(),
11098            range,
11099            evidence,
11100            parent,
11101            None,
11102        );
11103        // The recovery admitted this owner from an aggregate head and body.
11104        // Its malformed outer node need not itself be a struct_specifier, so
11105        // retain that structured definition proof explicitly.
11106        let declaration = *self.source_links[&owner]
11107            .last()
11108            .expect("collapsed aggregate has a source occurrence");
11109        {
11110            let mut properties = self.source_properties.borrow_mut();
11111            let fact = properties
11112                .get_mut(&declaration)
11113                .expect("collapsed aggregate retains its original context");
11114            fact.class_strength =
11115                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11116            fact.occurrence_role =
11117                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11118        }
11119        let owner_scope = ScopeInfo {
11120            class_unit: Some(owner),
11121            declarations_are_fields: true,
11122            ..scope.clone()
11123        };
11124        for member in members {
11125            match member {
11126                CppCollapsedMember::MacroFields {
11127                    range,
11128                    fields,
11129                    context,
11130                } => {
11131                    self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11132                }
11133                CppCollapsedMember::Declarations {
11134                    span,
11135                    start_line,
11136                    context,
11137                } => {
11138                    self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11139                }
11140            }
11141        }
11142    }
11143
11144    /// Recover an aggregate whose head tree-sitter folded into the declaration
11145    /// that precedes it, outside any collapsed `ERROR` container.
11146    ///
11147    /// A comment inside a field-list macro's replacement ends the replacement
11148    /// token, so the rest of the replacement is read at file scope. When the
11149    /// aggregate that follows declares its member list with one invocation,
11150    /// the parser reads the dangling tail as that declaration's type, the
11151    /// class key as an `ERROR`, and the member list as an initializer (#3098).
11152    /// The invocations before the class key belong to the replacement, not to
11153    /// this aggregate, so they go to whatever aggregate encloses it.
11154    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11155        if node.parent().is_some_and(|parent| parent.is_error()) {
11156            // The container's own recovery reads this child in source order,
11157            // with the preprocessor environment its earlier children build.
11158            return false;
11159        }
11160        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11161            return false;
11162        };
11163        let members = head
11164            .folded_members
11165            .expect("a folded aggregate head carries its member list");
11166        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11167        if name.is_empty() {
11168            return false;
11169        }
11170        let macro_nodes =
11171            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11172        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11173            .iter()
11174            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11175        for macro_node in preceding {
11176            let fields = object_macro_field_closure(
11177                &self.object_macro_fields,
11178                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11179            );
11180            self.materialize_object_macro_fields(
11181                fields,
11182                cpp_declaration_range(macro_node),
11183                macro_node,
11184                scope,
11185            );
11186        }
11187        let parent = scope.class_unit.clone();
11188        let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11189        let recovered = cpp_collapsed_aggregate_members(
11190            members,
11191            &inner,
11192            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11193            head.opening.end_position().row + 1,
11194            self.source,
11195            &self.object_macro_fields,
11196        );
11197        self.declare_collapsed_aggregate(
11198            owner,
11199            head.key.start_byte()..members.end_byte(),
11200            Some(node),
11201            parent,
11202            recovered,
11203            scope,
11204        );
11205        true
11206    }
11207
11208    /// Record the members of a collapsed aggregate region as fields of its
11209    /// recovered owner. The names, declaration text, and byte ranges all come
11210    /// from the grammar's reading of the region, so a region that is not a
11211    /// member list contributes nothing.
11212    fn record_collapsed_aggregate_fields(
11213        &mut self,
11214        span: std::ops::Range<usize>,
11215        start_line: usize,
11216        context: Node<'_>,
11217        scope: &ScopeInfo,
11218    ) {
11219        let Some(owner) = scope.class_unit.as_ref() else {
11220            return;
11221        };
11222        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11223            let range = Range {
11224                start_byte: field.range.start,
11225                end_byte: field.range.end,
11226                start_line: start_line
11227                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
11228                end_line: start_line
11229                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
11230            };
11231            let mut fq = owner.fq().clone();
11232            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11233            let short_name = if owner.short_name().is_empty() {
11234                field.name.clone()
11235            } else {
11236                format!("{}.{}", owner.short_name(), field.name)
11237            };
11238            let signature = normalize_cpp_whitespace(&field.declaration);
11239            let code_unit = CodeUnit::with_signature_and_fq(
11240                self.file.clone(),
11241                CodeUnitType::Field,
11242                owner.package_name().to_string(),
11243                short_name,
11244                Some(signature.clone()),
11245                false,
11246                fq,
11247            );
11248            if self.parsed.contains_declaration(&code_unit) {
11249                continue;
11250            }
11251            self.add_declaration_with_range(
11252                self.primary_ancestry,
11253                code_unit.clone(),
11254                range,
11255                None,
11256                Some(owner.clone()),
11257                None,
11258            );
11259            self.capture_recovered_field_facts(
11260                &code_unit,
11261                range,
11262                context,
11263                field.field_type,
11264                field.names_function_type,
11265            );
11266            self.parsed.add_signature(code_unit, signature);
11267        }
11268    }
11269
11270    fn visit_preproc_call(&mut self, node: Node<'_>) {
11271        let Some(_directive) = node.child_by_field_name("directive") else {
11272            return;
11273        };
11274        if is_cpp_undef_directive(node, self.source) {
11275            update_object_macro_field_environment(
11276                node,
11277                self.source,
11278                &mut self.object_macro_fields,
11279                &mut self.ambiguous_object_macro_fields,
11280            );
11281        }
11282    }
11283}
11284
11285/// Every member a field-list macro contributes under `environment`, including
11286/// the members of the field-list macros it composes.
11287///
11288/// Composition is resolved at the invocation, not at the definition, because a
11289/// nested name's active replacement is a property of the environment the
11290/// invocation sees. The traversal is iterative and visits each name once, so a
11291/// self-composing or mutually composing pair terminates.
11292fn object_macro_field_closure(
11293    environment: &HashMap<String, ObjectMacroReplacement>,
11294    name: &str,
11295) -> Vec<MacroReplacementField> {
11296    let mut fields = Vec::new();
11297    let mut visited = HashSet::default();
11298    let mut stack = vec![name.to_string()];
11299    while let Some(current) = stack.pop() {
11300        if !visited.insert(current.clone()) {
11301            continue;
11302        }
11303        let Some(replacement) = environment.get(&current) else {
11304            continue;
11305        };
11306        fields.extend(replacement.fields.iter().cloned());
11307        stack.extend(replacement.nested.iter().rev().cloned());
11308    }
11309    fields
11310}
11311
11312/// The structured replacement an object-like `#define` contributes.
11313///
11314/// The directive's whole logical line is the replacement list, which
11315/// [`object_macro_replacement_span`] recovers, because a comment inside the
11316/// replacement ends tree-sitter's `preproc_arg` token early.
11317fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11318    crate::graph::syntax::object_macro_replacement_span(node, source)
11319        .and_then(|span| source.get(span))
11320        .map(crate::graph::syntax::object_macro_replacement)
11321        .unwrap_or_default()
11322}
11323
11324/// Apply one preprocessor directive to an object-like field-list environment.
11325/// The environment is intentionally separate from the declaration visitor so
11326/// malformed parser regions can replay directives in source order without
11327/// mutating the live walk's state ahead of those directives.
11328fn update_object_macro_field_environment(
11329    node: Node<'_>,
11330    source: &str,
11331    fields: &mut HashMap<String, ObjectMacroReplacement>,
11332    ambiguous: &mut HashSet<String>,
11333) -> bool {
11334    match node.kind() {
11335        "preproc_def" => {
11336            let Some(name) = extract_macro_name(node, source) else {
11337                return false;
11338            };
11339            let replacement = object_macro_replacement_of(node, source);
11340            if replacement.is_empty() || ambiguous.contains(&name) {
11341                fields.remove(&name);
11342                ambiguous.insert(name);
11343            } else if let Some(previous) = fields.get(&name) {
11344                if previous != &replacement {
11345                    fields.remove(&name);
11346                    ambiguous.insert(name);
11347                }
11348            } else {
11349                fields.insert(name, replacement);
11350            }
11351            true
11352        }
11353        "preproc_call" if is_cpp_undef_directive(node, source) => {
11354            if let Some(argument) = node.child_by_field_name("argument") {
11355                let name = node_text(argument, source).trim();
11356                fields.remove(name);
11357                if inside_preprocessor_conditional(node) {
11358                    ambiguous.insert(name.to_string());
11359                } else {
11360                    ambiguous.remove(name);
11361                }
11362            }
11363            true
11364        }
11365        _ => false,
11366    }
11367}
11368
11369fn object_macro_identifier_nodes<'tree>(
11370    node: Node<'tree>,
11371    source: &str,
11372    fields: &HashMap<String, ObjectMacroReplacement>,
11373) -> Vec<Node<'tree>> {
11374    walk_object_macro_identifier_nodes(node, |current, result| {
11375        if matches!(
11376            current.kind(),
11377            "identifier" | "field_identifier" | "type_identifier"
11378        ) && fields.contains_key(node_text(current, source).trim())
11379        {
11380            result.push(current);
11381        }
11382        false
11383    })
11384}
11385
11386fn walk_object_macro_identifier_nodes<'tree>(
11387    node: Node<'tree>,
11388    mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11389) -> Vec<Node<'tree>> {
11390    let mut result = Vec::new();
11391    let mut stack = vec![node];
11392    while let Some(current) = stack.pop() {
11393        if visit(current, &mut result) {
11394            continue;
11395        }
11396        let mut cursor = current.walk();
11397        let children = current.children(&mut cursor).collect::<Vec<_>>();
11398        stack.extend(children.into_iter().rev());
11399    }
11400    result.sort_by_key(|node| node.start_byte());
11401    result
11402}
11403
11404/// Collect macro identifiers while replaying any nested preprocessor
11405/// directives in source order. Recovery regions can contain a later `#undef`
11406/// in the same parser error envelope; using the live visitor map for the
11407/// entire envelope would incorrectly materialize invocations after it.
11408fn object_macro_identifier_nodes_with_environment<'tree>(
11409    node: Node<'tree>,
11410    source: &str,
11411    fields: &mut HashMap<String, ObjectMacroReplacement>,
11412    ambiguous: &mut HashSet<String>,
11413) -> Vec<Node<'tree>> {
11414    walk_object_macro_identifier_nodes(node, |current, result| {
11415        if update_object_macro_field_environment(current, source, fields, ambiguous) {
11416            return true;
11417        }
11418        if matches!(
11419            current.kind(),
11420            "identifier" | "field_identifier" | "type_identifier"
11421        ) && fields.contains_key(node_text(current, source).trim())
11422        {
11423            result.push(current);
11424        }
11425        false
11426    })
11427}
11428
11429/// One member region of an aggregate whose head and body tree-sitter collapsed
11430/// into an `ERROR` container, in source order.
11431enum CppCollapsedMember<'tree> {
11432    /// The members a field-list macro invocation donates, all sharing the
11433    /// invocation's range.
11434    MacroFields {
11435        context: Node<'tree>,
11436        range: Range,
11437        fields: Vec<MacroReplacementField>,
11438    },
11439    /// A byte span whose ordinary member declarations lost their shape with
11440    /// the aggregate body, with the 1-based line the span starts on.
11441    Declarations {
11442        context: Node<'tree>,
11443        span: std::ops::Range<usize>,
11444        start_line: usize,
11445    },
11446}
11447
11448/// One aggregate head recovered from the children of a collapsed `ERROR`
11449/// container.
11450///
11451/// A member list that opens with an object-like field-list macro invocation
11452/// has no grammar rule, so the aggregate never forms a specifier and its head
11453/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
11454/// class key, the name, and the opening brace are three consecutive children.
11455/// When the aggregate follows the dangling tail of a comment-truncated
11456/// replacement, the parser reads that tail as the declaration's type and folds
11457/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
11458/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
11459/// }` that follows becomes one `field_declaration` whose class key is an
11460/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
11461/// `initializer_list` (#3098).
11462struct CppCollapsedAggregateHead<'tree> {
11463    key: Node<'tree>,
11464    name: Node<'tree>,
11465    opening: Node<'tree>,
11466    /// The member list when the head folded it into its own node, which also
11467    /// carries the aggregate's closing brace. `None` leaves the aggregate open
11468    /// for the children that follow it.
11469    folded_members: Option<Node<'tree>>,
11470    /// How many children the head consumes.
11471    width: usize,
11472}
11473
11474/// The aggregate head that starts at `children[index]`, if one does.
11475fn cpp_collapsed_aggregate_head<'tree>(
11476    children: &[Node<'tree>],
11477    index: usize,
11478    source: &str,
11479) -> Option<CppCollapsedAggregateHead<'tree>> {
11480    let key = *children.get(index)?;
11481    if matches!(key.kind(), "struct" | "class" | "union") {
11482        let name = *children.get(index + 1)?;
11483        let opening = *children.get(index + 2)?;
11484        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11485            return None;
11486        }
11487        return Some(CppCollapsedAggregateHead {
11488            key,
11489            name,
11490            opening,
11491            folded_members: None,
11492            width: 3,
11493        });
11494    }
11495    cpp_folded_aggregate_head(key, source)
11496}
11497
11498/// The aggregate head tree-sitter folded into one declaration node, reading
11499/// the class key as an `ERROR` and the member list as an initializer.
11500fn cpp_folded_aggregate_head<'tree>(
11501    node: Node<'tree>,
11502    source: &str,
11503) -> Option<CppCollapsedAggregateHead<'tree>> {
11504    if !matches!(node.kind(), "declaration" | "field_declaration") {
11505        return None;
11506    }
11507    let mut cursor = node.walk();
11508    let children = node.children(&mut cursor).collect::<Vec<_>>();
11509    let key_index = children.iter().position(|child| {
11510        child.is_error()
11511            && matches!(
11512                node_text(*child, source).trim(),
11513                "struct" | "class" | "union"
11514            )
11515    })?;
11516    let declarator = *children.get(key_index + 1)?;
11517    // At file scope the grammar has an `init_declarator` for the name and its
11518    // initializer; inside an `ERROR` container the two are loose siblings.
11519    let (name, members) = if declarator.kind() == "init_declarator" {
11520        (
11521            declarator.child_by_field_name("declarator")?,
11522            declarator.child_by_field_name("value")?,
11523        )
11524    } else {
11525        (declarator, *children.get(key_index + 2)?)
11526    };
11527    if !matches!(
11528        name.kind(),
11529        "field_identifier" | "type_identifier" | "identifier"
11530    ) {
11531        return None;
11532    }
11533    if members.kind() != "initializer_list" {
11534        return None;
11535    }
11536    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11537    Some(CppCollapsedAggregateHead {
11538        key: children[key_index],
11539        name,
11540        opening,
11541        folded_members: Some(members),
11542        width: 1,
11543    })
11544}
11545
11546/// Where a folded member list ends: at its own closing brace when it has one,
11547/// so the reparse of the declarations it holds sees a member list and not a
11548/// stray brace.
11549fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11550    let mut cursor = members.walk();
11551    members
11552        .children(&mut cursor)
11553        .filter(|child| child.kind() == "}" && !child.is_missing())
11554        .last()
11555        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11556}
11557
11558/// The members one collapsed aggregate region declares, in source order: what
11559/// its field-list macro invocations donate, then the ordinary declarations
11560/// that follow the last invocation and lost their shape with the body.
11561fn cpp_collapsed_aggregate_members<'tree>(
11562    context: Node<'tree>,
11563    macro_nodes: &[Node<'tree>],
11564    region: std::ops::Range<usize>,
11565    region_start_line: usize,
11566    source: &str,
11567    environment: &HashMap<String, ObjectMacroReplacement>,
11568) -> Vec<CppCollapsedMember<'tree>> {
11569    let mut members = macro_nodes
11570        .iter()
11571        .map(|macro_node| CppCollapsedMember::MacroFields {
11572            context: *macro_node,
11573            range: cpp_declaration_range(*macro_node),
11574            fields: object_macro_field_closure(
11575                environment,
11576                &normalize_cpp_whitespace(node_text(*macro_node, source)),
11577            ),
11578        })
11579        .collect::<Vec<_>>();
11580    // Whatever follows the last invocation is an ordinary member list that
11581    // lost its declaration shape with the aggregate's body; the grammar
11582    // recovers it from its own byte range.
11583    let declarations_start = macro_nodes
11584        .last()
11585        .map_or(region.start, |macro_node| macro_node.end_byte());
11586    members.push(CppCollapsedMember::Declarations {
11587        context,
11588        span: declarations_start..region.end,
11589        start_line: region_start_line
11590            + cpp_line_breaks_between(source, region.start, declarations_start),
11591    });
11592    members
11593}
11594
11595/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
11596/// line without rescanning the file from its start.
11597fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11598    source.get(from..to).map_or(0, |slice| {
11599        slice.bytes().filter(|byte| *byte == b'\n').count()
11600    })
11601}
11602
11603/// How many enclosing aggregates a collapsed member declaration closes: the
11604/// close braces it carries that its own braces do not balance. A member that
11605/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
11606/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
11607/// owner early and lost every member after it (#2985).
11608fn count_close_brace_nodes(node: Node<'_>) -> usize {
11609    let mut opened = 0usize;
11610    let mut closed = 0usize;
11611    let mut stack = vec![node];
11612    while let Some(current) = stack.pop() {
11613        if !current.is_missing() {
11614            match current.kind() {
11615                "{" => opened += 1,
11616                "}" => closed += 1,
11617                _ => {}
11618            }
11619        }
11620        let mut cursor = current.walk();
11621        stack.extend(current.children(&mut cursor));
11622    }
11623    closed.saturating_sub(opened)
11624}
11625
11626/// Classify a C++ field while its declaration syntax is already available.
11627///
11628/// The persisted result lets later visibility queries avoid reparsing the
11629/// complete source file only to recover linkage.
11630pub fn cpp_field_declaration_linkage<'tree>(
11631    declaration: Node<'tree>,
11632    source: &str,
11633    ancestry: &ParentIndex<'tree>,
11634) -> CppFieldLinkage {
11635    let mut current = ancestry.parent(declaration);
11636    let mut enclosed_by_class = false;
11637    while let Some(node) = current {
11638        if node.kind() == "namespace_definition"
11639            && node
11640                .child_by_field_name("name")
11641                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11642        {
11643            return CppFieldLinkage::Internal;
11644        }
11645        if matches!(
11646            node.kind(),
11647            "class_specifier" | "struct_specifier" | "union_specifier"
11648        ) && node
11649            .child_by_field_name("name")
11650            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11651        {
11652            return CppFieldLinkage::Internal;
11653        }
11654        if matches!(
11655            node.kind(),
11656            "class_specifier" | "struct_specifier" | "union_specifier"
11657        ) {
11658            enclosed_by_class = true;
11659        }
11660        if matches!(node.kind(), "function_definition" | "lambda_expression") {
11661            return CppFieldLinkage::Internal;
11662        }
11663        current = ancestry.parent(node);
11664    }
11665    if enclosed_by_class {
11666        return CppFieldLinkage::External;
11667    }
11668    let mut cursor = declaration.walk();
11669    let mut has_static = false;
11670    let mut has_extern = false;
11671    let mut has_inline = false;
11672    let mut has_const = false;
11673    let mut has_constexpr = false;
11674    for child in declaration.named_children(&mut cursor) {
11675        let text = normalize_cpp_whitespace(node_text(child, source));
11676        match (child.kind(), text.as_str()) {
11677            ("storage_class_specifier", "static") => has_static = true,
11678            ("storage_class_specifier", "extern") => has_extern = true,
11679            ("storage_class_specifier", "inline") => has_inline = true,
11680            ("storage_class_specifier", "constexpr") => has_constexpr = true,
11681            ("type_qualifier", "const") => has_const = true,
11682            ("type_qualifier", "constexpr") => has_constexpr = true,
11683            _ => {}
11684        }
11685    }
11686    if has_static {
11687        CppFieldLinkage::Internal
11688    } else if has_extern || has_inline {
11689        CppFieldLinkage::External
11690    } else if has_const || has_constexpr {
11691        CppFieldLinkage::InternalUnlessExternalPeer
11692    } else {
11693        CppFieldLinkage::External
11694    }
11695}
11696
11697fn cpp_declaration_range(node: Node<'_>) -> Range {
11698    Range {
11699        start_byte: node.start_byte(),
11700        end_byte: node.end_byte(),
11701        start_line: node.start_position().row + 1,
11702        end_line: node.end_position().row + 1,
11703    }
11704}
11705
11706/// A recovery interval as a [`Range`], for materialization records whose
11707/// window is a byte region rather than one parser node (the sentinel-macro
11708/// region reparses, issue #941/#1657).
11709fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11710    let line_at = |byte: usize| {
11711        source.as_bytes()[..byte]
11712            .iter()
11713            .filter(|&&b| b == b'\n')
11714            .count()
11715            + 1
11716    };
11717    Range {
11718        start_byte,
11719        end_byte,
11720        start_line: line_at(start_byte),
11721        end_line: line_at(end_byte),
11722    }
11723}
11724
11725fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11726    ImportInfo {
11727        raw_snippet,
11728        is_wildcard: false,
11729        is_global: false,
11730        identifier: None,
11731        alias: None,
11732        path: None,
11733        binder_span: None,
11734    }
11735}
11736
11737fn recover_quoted_source_imports(
11738    source: &str,
11739    imports: &mut Vec<SourceImportFact>,
11740    includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11741    collector: &RefCell<PrimarySourceFactCollector<'_>>,
11742) {
11743    let mut in_block_comment = false;
11744    let mut start = 0;
11745    for line in source.split_inclusive('\n') {
11746        let end = start + line.len();
11747        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11748        let trimmed = stripped.trim();
11749        if looks_like_quoted_include_line(trimmed) {
11750            let raw = normalize_cpp_whitespace(trimmed);
11751            if !imports.iter().any(|import| import.statement == raw) {
11752                let occurrence = collector.borrow_mut().intern_subspan_bytes(
11753                    start,
11754                    end,
11755                    SourceOccurrenceProvenance::ExplicitSubspan,
11756                );
11757                // Recovery already identified a quoted directive hidden by a
11758                // malformed outer construct. Interpret its path with the same
11759                // grammar in an isolated included range, preserving byte identity.
11760                let region =
11761                    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11762                let target = region.as_ref().and_then(|tree| {
11763                    let mut pending = vec![tree.root_node()];
11764                    while let Some(node) = pending.pop() {
11765                        if node.kind() == "preproc_include"
11766                            && let Some(path) = node.child_by_field_name("path")
11767                            && path.kind() == "string_literal"
11768                        {
11769                            let (value, _) = crate::imports::include_path_from_node(node, source)?;
11770                            let target = collector.borrow_mut().intern_embedded(
11771                                brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11772                            );
11773                            includes.push(
11774                                brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11775                                    declaration: occurrence,
11776                                    target,
11777                                    path: value.to_owned(),
11778                                    quoted: true,
11779                                },
11780                            );
11781                            return Some(target);
11782                        }
11783                        let mut cursor = node.walk();
11784                        let children: Vec<_> = node.named_children(&mut cursor).collect();
11785                        pending.extend(children.into_iter().rev());
11786                    }
11787                    None
11788                });
11789                imports.push(SourceImportFact::from_import(
11790                    cpp_include_info(raw),
11791                    occurrence,
11792                    target,
11793                    None,
11794                    Vec::new(),
11795                ));
11796            }
11797        }
11798        start = end;
11799    }
11800}
11801
11802fn looks_like_quoted_include_line(line: &str) -> bool {
11803    let Some(rest) = line.trim_start().strip_prefix('#') else {
11804        return false;
11805    };
11806    let Some(rest) = rest.trim_start().strip_prefix("include") else {
11807        return false;
11808    };
11809    rest.trim_start().starts_with('"')
11810}
11811
11812fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11813    let mut raw = Vec::new();
11814    if let Some(bases) = cpp_class_like_base_clause(node) {
11815        collect_cpp_base_nodes(bases, source, &mut raw);
11816    }
11817    raw
11818}
11819
11820fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11821    let mut facts = Vec::new();
11822    let mut cursor = node.walk();
11823    for child in node.named_children(&mut cursor) {
11824        if child.kind() != "base_class_clause" {
11825            continue;
11826        }
11827        let mut is_virtual = false;
11828        let mut child_cursor = child.walk();
11829        for base in child.children(&mut child_cursor) {
11830            match base.kind() {
11831                "virtual" => is_virtual = true,
11832                "," => is_virtual = false,
11833                "type_identifier"
11834                | "qualified_identifier"
11835                | "scoped_type_identifier"
11836                | "template_type"
11837                | "template_function" => {
11838                    if let Some(fact) = crate::source_facts::base_specifier_from_node(
11839                        base,
11840                        source,
11841                        is_virtual,
11842                        crate::source_facts::base_access_specifier(child, base, node.kind()),
11843                    ) {
11844                        facts.push(fact);
11845                    }
11846                    is_virtual = false;
11847                }
11848                _ => {}
11849            }
11850        }
11851    }
11852    facts
11853}
11854
11855/// The `base_class_clause` a class-like node writes, if it writes one.
11856///
11857/// The pinned grammar exposes the clause as a named child rather than a field,
11858/// so the question is asked by kind. A base clause is the only place a C++
11859/// class body records that some other class may declare a member, which is
11860/// what [`cpp_callable_dispatch_extensibility`] reads it for.
11861fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11862    let mut cursor = class_like.walk();
11863    class_like
11864        .named_children(&mut cursor)
11865        .find(|child| child.kind() == "base_class_clause")
11866}
11867
11868fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11869    walk_named_tree_preorder(node, false, |child| match child.kind() {
11870        "type_identifier" | "qualified_identifier" | "template_type" => {
11871            let text = normalize_cpp_whitespace(node_text(child, source));
11872            if !text.is_empty() {
11873                raw.push(text);
11874            }
11875            WalkControl::SkipChildren
11876        }
11877        _ => WalkControl::Continue,
11878    });
11879}
11880
11881fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11882    let mut out = String::new();
11883    let chars: Vec<char> = line.chars().collect();
11884    let mut index = 0;
11885    let mut in_string = false;
11886    let mut in_char = false;
11887    let mut escape = false;
11888
11889    while index < chars.len() {
11890        let ch = chars[index];
11891        let next = chars.get(index + 1).copied();
11892
11893        if *in_block_comment {
11894            if ch == '*' && next == Some('/') {
11895                *in_block_comment = false;
11896                index += 2;
11897            } else {
11898                index += 1;
11899            }
11900            continue;
11901        }
11902
11903        if in_string {
11904            out.push(ch);
11905            if escape {
11906                escape = false;
11907            } else if ch == '\\' {
11908                escape = true;
11909            } else if ch == '"' {
11910                in_string = false;
11911            }
11912            index += 1;
11913            continue;
11914        }
11915
11916        if in_char {
11917            out.push(ch);
11918            if escape {
11919                escape = false;
11920            } else if ch == '\\' {
11921                escape = true;
11922            } else if ch == '\'' {
11923                in_char = false;
11924            }
11925            index += 1;
11926            continue;
11927        }
11928
11929        if ch == '/' && next == Some('/') {
11930            break;
11931        }
11932        if ch == '/' && next == Some('*') {
11933            *in_block_comment = true;
11934            index += 2;
11935            continue;
11936        }
11937        if ch == '"' {
11938            in_string = true;
11939            out.push(ch);
11940            index += 1;
11941            continue;
11942        }
11943        if ch == '\'' {
11944            in_char = true;
11945            out.push(ch);
11946            index += 1;
11947            continue;
11948        }
11949
11950        out.push(ch);
11951        index += 1;
11952    }
11953
11954    out
11955}
11956
11957#[derive(Clone)]
11958struct FunctionInfo {
11959    package_name: String,
11960    owner: Option<CppMemberOwner>,
11961    name: String,
11962    signature: String,
11963}
11964
11965/// Owner of a member function, kept structured so a literal `$` inside a
11966/// source-level class name never crosses a join/split boundary: the legacy
11967/// `$`-joined owner string was re-split at fq construction, dropping a leading
11968/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
11969/// and tripping the package/short boundary assert -- the #2140 corruption one
11970/// level up (#2362).
11971#[derive(Clone)]
11972enum CppMemberOwner {
11973    /// Source-level owner class chain from a qualified declarator-id, one
11974    /// class name per component (`Outer::Inner::method` -> `["Outer",
11975    /// "Inner"]`); each component may itself contain a literal `$`.
11976    Chain(Vec<String>),
11977    /// The lexically enclosing or recovered class unit; the member fq extends
11978    /// its fq directly instead of re-splitting its `$`-joined short chain.
11979    Unit(CodeUnit),
11980}
11981
11982impl CppMemberOwner {
11983    /// The legacy `$`-joined owner chain used in the member's short name.
11984    fn short_chain(&self) -> String {
11985        match self {
11986            Self::Chain(chain) => chain.join("$"),
11987            Self::Unit(parent) => parent.short_name().to_string(),
11988        }
11989    }
11990}
11991
11992enum DeclaratorKind<'a> {
11993    Function(Node<'a>),
11994    Variable(Node<'a>),
11995}
11996
11997impl FunctionInfo {
11998    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
11999        self.code_unit_with_synthetic(file, false)
12000    }
12001
12002    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
12003        let short_name = match &self.owner {
12004            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
12005            None => self.name.clone(),
12006        };
12007        let fq = match &self.owner {
12008            Some(CppMemberOwner::Chain(chain)) => {
12009                debug_assert!(
12010                    !chain.is_empty(),
12011                    "an empty owner chain is no owner; producers return None instead"
12012                );
12013                let mut fq = FqName::new();
12014                cpp_push_package(&mut fq, &self.package_name);
12015                let mut first = true;
12016                for component in chain {
12017                    let kind = if first {
12018                        SegmentKind::Type
12019                    } else {
12020                        SegmentKind::Nested
12021                    };
12022                    fq.push(segment_interner().intern(component, kind));
12023                    first = false;
12024                }
12025                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12026                fq
12027            }
12028            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
12029                .fq()
12030                .clone()
12031                .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
12032            // An anonymous parent (empty short chain) contributes no owner
12033            // segment -- the same guard as cpp_join_member_short above.
12034            Some(CppMemberOwner::Unit(_)) | None => {
12035                let mut fq = FqName::new();
12036                cpp_push_package(&mut fq, &self.package_name);
12037                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12038                fq
12039            }
12040        };
12041        CodeUnit::with_signature_and_fq(
12042            file,
12043            CodeUnitType::Function,
12044            self.package_name.clone(),
12045            short_name,
12046            Some(self.signature.clone()),
12047            synthetic,
12048            fq,
12049        )
12050    }
12051}
12052
12053fn extract_function_info(
12054    declarator: Node<'_>,
12055    source: &str,
12056    scope: &ScopeInfo,
12057) -> Option<FunctionInfo> {
12058    let parameters_node = declarator.child_by_field_name("parameters")?;
12059    let declarator_name_node = declarator
12060        .child_by_field_name("declarator")
12061        .or_else(|| parameters_node.prev_named_sibling())?;
12062    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
12063}
12064
12065fn extract_function_info_from_name(
12066    declarator: Node<'_>,
12067    declarator_name_node: Node<'_>,
12068    source: &str,
12069    scope: &ScopeInfo,
12070) -> Option<FunctionInfo> {
12071    let parameters_node = declarator.child_by_field_name("parameters")?;
12072    let parameters_text = cpp_parameter_signature(parameters_node, source);
12073    let recovered_specialization_member = scope
12074        .recovered_specialization_member_scope
12075        .then(|| {
12076            let terminal = declarator_name_node
12077                .child_by_field_name("name")
12078                .unwrap_or(declarator_name_node);
12079            let name = canonical_cpp_qualified_component(terminal, source)?.name;
12080            let owner = scope.class_unit.as_ref()?;
12081            Some((
12082                Some(CppMemberOwner::Unit(owner.clone())),
12083                name,
12084                scope.package_name.clone(),
12085            ))
12086        })
12087        .flatten();
12088    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
12089        parts
12090    } else if let Some(parts) =
12091        split_structured_templated_cpp_name(declarator, declarator_name_node, source, scope)
12092    {
12093        parts
12094    } else {
12095        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
12096            declarator_name_node,
12097            source,
12098        )?);
12099        if raw_name.is_empty() {
12100            return None;
12101        }
12102        let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
12103        match using_bound_cpp_owner(declarator_name_node, source, scope) {
12104            Some((owner_chain, package_name)) => {
12105                (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
12106            }
12107            None => (owner, name, package_name),
12108        }
12109    };
12110    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
12111    let mut signature = if suffix.is_empty() {
12112        parameters_text
12113    } else {
12114        format!("{parameters_text} {suffix}")
12115    };
12116    if let Some(template_signature) = &scope.template_signature {
12117        signature = format!("{template_signature}{signature}");
12118    }
12119
12120    Some(FunctionInfo {
12121        package_name,
12122        owner,
12123        name,
12124        signature,
12125    })
12126}
12127
12128/// Recover the semantic return type and callable name when a declaration macro
12129/// occupies a function definition's `type` field. Tree-sitter either exposes a
12130/// scalar return as the declarator's apparent name and the callable as the sole
12131/// identifier in an `ERROR`, or joins a template return and callable into a
12132/// qualified identifier with a missing `::`. Both shapes retain the complete
12133/// parameter list and body; a concrete separator remains an out-of-line member.
12134fn cpp_macro_displaced_callable_parts<'tree>(
12135    function_declarator: Node<'tree>,
12136    source: &str,
12137    ancestry: &ParentIndex<'tree>,
12138) -> Option<(Node<'tree>, Node<'tree>)> {
12139    let definition = ancestry.parent(function_declarator)?;
12140    if definition.kind() != "function_definition"
12141        || definition.child_by_field_name("declarator") != Some(function_declarator)
12142        || definition
12143            .child_by_field_name("body")
12144            .is_none_or(|body| body.kind() != "compound_statement")
12145    {
12146        return None;
12147    }
12148    let macro_type = definition.child_by_field_name("type")?;
12149    if macro_type.kind() != "type_identifier"
12150        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12151    {
12152        return None;
12153    }
12154
12155    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12156    if apparent_return_type.kind() == "qualified_identifier"
12157        && let (Some(return_type), Some(callable_name)) = (
12158            apparent_return_type.child_by_field_name("scope"),
12159            apparent_return_type.child_by_field_name("name"),
12160        )
12161        && return_type.kind() == "template_type"
12162        && matches!(callable_name.kind(), "identifier" | "field_identifier")
12163        && (0..apparent_return_type.child_count())
12164            .filter_map(|index| apparent_return_type.child(index))
12165            .any(|child| child.kind() == "::" && child.is_missing())
12166        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12167        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12168    {
12169        return Some((return_type, callable_name));
12170    }
12171    if !matches!(
12172        apparent_return_type.kind(),
12173        "identifier" | "field_identifier" | "type_identifier"
12174    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12175    {
12176        return None;
12177    }
12178    let parameters = function_declarator.child_by_field_name("parameters")?;
12179    let mut cursor = function_declarator.walk();
12180    let between = function_declarator
12181        .named_children(&mut cursor)
12182        .filter(|child| child.kind() != "comment")
12183        .filter(|child| {
12184            child.start_byte() >= apparent_return_type.end_byte()
12185                && child.end_byte() <= parameters.start_byte()
12186                && !same_node(*child, apparent_return_type)
12187                && !same_node(*child, parameters)
12188        })
12189        .collect::<Vec<_>>();
12190    let [name_error] = between.as_slice() else {
12191        return None;
12192    };
12193    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12194        return None;
12195    }
12196    let callable_name = name_error.named_child(0)?;
12197    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12198        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12199    {
12200        return None;
12201    }
12202    Some((apparent_return_type, callable_name))
12203}
12204
12205/// The part of a `function_declarator` after its parameter list that belongs to
12206/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
12207/// specification, a trailing return type and a trailing requires-clause.
12208///
12209/// The grammar makes each of these a distinct sibling of the `parameters`
12210/// field, so they are read from the tree. Splitting the declarator's text on
12211/// the parameter list instead silently dropped every qualifier whenever the
12212/// parameter list was spelled with whitespace that normalization rewrote - a
12213/// line break or a double space was enough to make a `const` member definition
12214/// a different logical symbol from its declaration (#1827).
12215///
12216/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
12217/// are deliberately excluded. C++ does not make them part of the signature and
12218/// an out-of-line definition never repeats them, so including them would split
12219/// a declaration from its own definition.
12220fn cpp_declarator_identity_suffix(
12221    declarator: Node<'_>,
12222    parameters_node: Node<'_>,
12223    source: &str,
12224) -> String {
12225    let mut cursor = declarator.walk();
12226    let parts = declarator
12227        .named_children(&mut cursor)
12228        .filter(|child| child.start_byte() >= parameters_node.end_byte())
12229        .filter(|child| {
12230            matches!(
12231                child.kind(),
12232                "type_qualifier"
12233                    | "ref_qualifier"
12234                    | "noexcept"
12235                    | "throw_specifier"
12236                    | "trailing_return_type"
12237                    | "requires_clause"
12238            )
12239        })
12240        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12241        .filter(|text| !text.is_empty())
12242        .collect::<Vec<_>>();
12243    normalize_cpp_qualifier_suffix(&parts.join(" "))
12244}
12245
12246/// The identity suffix of one callable declarator, for a consumer that holds
12247/// the declarator rather than the declaration walk's parts.
12248///
12249/// The persisted signature concatenates the parameter spelling and this suffix,
12250/// so a comparison that must agree on the suffix alone recomputes it here
12251/// instead of splitting the stored string.
12252pub(crate) fn cpp_callable_identity_suffix(
12253    function_declarator: Node<'_>,
12254    source: &str,
12255) -> Option<String> {
12256    let parameters_node = function_declarator.child_by_field_name("parameters")?;
12257    Some(cpp_declarator_identity_suffix(
12258        function_declarator,
12259        parameters_node,
12260        source,
12261    ))
12262}
12263
12264pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12265    match classify_declarator(node)? {
12266        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12267        DeclaratorKind::Variable(_) => None,
12268    }
12269}
12270
12271fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12272    match node.kind() {
12273        "function_declarator" => {
12274            let inner = node
12275                .child_by_field_name("declarator")
12276                .or_else(|| node.child_by_field_name("name"))
12277                .or_else(|| last_named_child(node));
12278            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12279                Some(DeclaratorKind::Variable(node))
12280            } else {
12281                Some(DeclaratorKind::Function(node))
12282            }
12283        }
12284        // A macro marker is declaration provenance, never a declarator: its
12285        // identifier names the macro, not anything the declaration declares.
12286        "macro_modifier" => None,
12287        "init_declarator"
12288        | "pointer_declarator"
12289        | "reference_declarator"
12290        | "parenthesized_declarator"
12291        | "array_declarator"
12292        | "attributed_declarator"
12293        | "template_function" => node
12294            .child_by_field_name("declarator")
12295            .or_else(|| node.child_by_field_name("name"))
12296            .or_else(|| last_named_child(node))
12297            .and_then(classify_declarator),
12298        "identifier" | "field_identifier" | "qualified_identifier" => {
12299            Some(DeclaratorKind::Variable(node))
12300        }
12301        _ => node
12302            .child_by_field_name("declarator")
12303            .or_else(|| node.child_by_field_name("name"))
12304            .or_else(|| last_named_child(node))
12305            .and_then(classify_declarator),
12306    }
12307}
12308
12309fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12310    matches!(
12311        node.kind(),
12312        "function_declarator"
12313            | "init_declarator"
12314            | "pointer_declarator"
12315            | "reference_declarator"
12316            | "parenthesized_declarator"
12317            | "array_declarator"
12318            | "attributed_declarator"
12319            | "template_function"
12320            | "macro_modifier_declarator"
12321            | "identifier"
12322            | "field_identifier"
12323            | "qualified_identifier"
12324    )
12325}
12326
12327fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12328    let class_like = first_class_like_child(node);
12329    let mut cursor = node.walk();
12330    node.named_children(&mut cursor).any(|child| {
12331        matches!(
12332            child.kind(),
12333            "init_declarator"
12334                | "pointer_declarator"
12335                | "reference_declarator"
12336                | "array_declarator"
12337                | "function_declarator"
12338                | "parenthesized_declarator"
12339                | "attributed_declarator"
12340        ) || matches!(
12341            child.kind(),
12342            "identifier" | "field_identifier" | "qualified_identifier"
12343        ) && class_like.is_none_or(|class_node| {
12344            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12345        })
12346    })
12347}
12348
12349/// One namespace-scope forward class declaration that a recovered export-macro
12350/// class definition may borrow its identity from.  Tree-sitter can close a
12351/// malformed class at the enclosing namespace's closing brace, leaving the later
12352/// class definitions as root-level recovered `function_definition` nodes.  A
12353/// preceding `class Name;` in the same namespace is the only structured identity
12354/// signal available in that shape.
12355///
12356/// Everything recorded here is a property of the forward declaration alone.
12357/// What depends on the node doing the asking -- that the forward and its
12358/// namespace both close before it, with nothing but recovery trivia between --
12359/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
12360/// tree answers every later question about it.
12361struct CppNamespaceForward {
12362    name: String,
12363    start_byte: usize,
12364    /// Where the malformed namespace that held the forward ended.  No query
12365    /// asks anything else about that node.
12366    namespace_end_byte: usize,
12367    package_name: String,
12368}
12369
12370/// Read `node` as a borrowable namespace forward declaration.
12371///
12372/// The admission is deliberately conservative: only a body-less class specifier
12373/// whose declaration has no declarator, at namespace scope rather than inside a
12374/// function or class body, in a namespace that itself failed to parse.
12375fn cpp_namespace_forward_entry<'tree>(
12376    node: Node<'tree>,
12377    source: &str,
12378    ancestry: &ParentIndex<'tree>,
12379) -> Option<CppNamespaceForward> {
12380    if !matches!(
12381        node.kind(),
12382        "class_specifier" | "struct_specifier" | "union_specifier"
12383    ) || cpp_body_node(node).is_some()
12384    {
12385        return None;
12386    }
12387    let parent = node.parent()?;
12388    if !(parent.kind() == "declaration_list"
12389        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12390    {
12391        return None;
12392    }
12393    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12394    // Borrowing is only justified by the parser-recovery shape we are
12395    // repairing: the namespace that held the forward must itself contain a
12396    // syntax error. A clean, unrelated namespace forward is not an identity
12397    // proof.
12398    if !namespace.has_error() {
12399        return None;
12400    }
12401    Some(CppNamespaceForward {
12402        name: class_like_name(node, source, ancestry)?,
12403        start_byte: node.start_byte(),
12404        namespace_end_byte: namespace.end_byte(),
12405        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12406    })
12407}
12408
12409/// Whether `forward` stands in the recovery relation to the node asking about
12410/// it: it and its malformed namespace both closed before the recovered class,
12411/// and nothing but recovery trivia separates the two.
12412fn cpp_namespace_forward_matches_recovery(
12413    forward: &CppNamespaceForward,
12414    recovered_node: Node<'_>,
12415) -> bool {
12416    forward.start_byte < recovered_node.start_byte()
12417        && forward.namespace_end_byte < recovered_node.start_byte()
12418        && malformed_namespace_is_nearest_recovery_region(
12419            forward.namespace_end_byte,
12420            recovered_node,
12421        )
12422}
12423
12424/// What one open [`CppVisitor::record_recovered_declarations`] has watched
12425/// happen to the declaration set.
12426///
12427/// The recovered set used to be a difference against a clone of the whole
12428/// declaration set, taken once per recovery: O(recoveries x declarations) on
12429/// exactly the error-recovered files that already walk slowest (#2787). The
12430/// walk knows which declarations it creates, so the capture collects them as
12431/// they are made and the difference is never needed.
12432///
12433/// `removed_pre_existing` is what makes that equal to the difference. A
12434/// deferred replacement removes the replaced declaration's children
12435/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
12436/// re-creates them. Creation alone cannot tell that apart from a first mint, so
12437/// a removal of a declaration this capture did not create records that it was
12438/// already there when the capture opened.
12439#[derive(Debug, Default)]
12440pub struct CppRecoveryCapture {
12441    /// Declarations created while this capture was open, in creation order.
12442    created: Vec<CodeUnit>,
12443    /// Membership for `created`.
12444    created_units: HashSet<CodeUnit>,
12445    /// Declarations that predate this capture and have been removed during it.
12446    removed_pre_existing: HashSet<CodeUnit>,
12447}
12448
12449/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
12450///
12451/// A region reparse is its own tree and is dropped while the walk that made it
12452/// continues, so a later parse can be allocated at the same address and hand out
12453/// the same node ids.  The root's span and shape pin the identity its address
12454/// alone does not: two roots agreeing on all of this are the same parse of the
12455/// same bytes, and a scan of one is a scan of the other.
12456#[derive(PartialEq, Eq, Hash)]
12457pub struct CppTreeIdentity {
12458    root_id: usize,
12459    start_byte: usize,
12460    end_byte: usize,
12461    kind_id: u16,
12462    child_count: usize,
12463}
12464
12465impl CppTreeIdentity {
12466    fn of(root: Node<'_>) -> Self {
12467        Self {
12468            root_id: root.id(),
12469            start_byte: root.start_byte(),
12470            end_byte: root.end_byte(),
12471            kind_id: root.kind_id(),
12472            child_count: root.child_count(),
12473        }
12474    }
12475}
12476
12477/// The namespace forward declarations one tree's prefix holds, folded in as the
12478/// walk asks about them.
12479///
12480/// `scope_for_recovered_exported_class` asks the same question once per
12481/// recovered class, and the answer depends only on the part of the tree that
12482/// starts before the asking node.  Rescanning that prefix per question is
12483/// quadratic in the file, and a generated header whose parse recovery leaves
12484/// thousands of class-like declarations at file scope pays all of it: 1,904
12485/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
12486/// (#2754).  This carries the scan forward instead.  Each question advances the
12487/// traversal from wherever the last one stopped to the asking node's start byte,
12488/// so a whole walk pays at most one pass over the prefix its furthest question
12489/// reaches, and each question then costs a name lookup.
12490#[derive(Default)]
12491pub struct CppNamespaceForwardScan {
12492    /// Every named node starting before this byte has been folded in.
12493    scanned_through: usize,
12494    forwards: HashMap<String, Vec<CppNamespaceForward>>,
12495}
12496
12497impl CppNamespaceForwardScan {
12498    /// Fold in every named node of `root` that starts at or after the fold
12499    /// watermark and before `cutoff`.
12500    ///
12501    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
12502    /// pass owes are exactly the ones no earlier pass reached, and a question
12503    /// about an earlier byte than one already answered costs nothing.
12504    fn advance_to<'tree>(
12505        &mut self,
12506        root: Node<'tree>,
12507        cutoff: usize,
12508        source: &str,
12509        ancestry: &ParentIndex<'tree>,
12510    ) {
12511        if cutoff <= self.scanned_through {
12512            return;
12513        }
12514        let folded_through = self.scanned_through;
12515        let mut cursor = root.walk();
12516        let mut stack = vec![root];
12517        while let Some(current) = stack.pop() {
12518            if (folded_through..cutoff).contains(&current.start_byte())
12519                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12520            {
12521                self.forwards
12522                    .entry(forward.name.clone())
12523                    .or_default()
12524                    .push(forward);
12525            }
12526            // A subtree ending before the watermark holds only nodes an earlier
12527            // pass already folded, and one starting at or after the cutoff is
12528            // outside the prefix being asked about. Skipping both is what keeps
12529            // the total traversal to one pass.
12530            for child in current.named_children(&mut cursor) {
12531                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12532                    stack.push(child);
12533                }
12534            }
12535        }
12536        self.scanned_through = cutoff;
12537    }
12538
12539    /// The one namespace `name` was forward declared in before `recovered_node`.
12540    /// More than one matching forward declaration is ambiguous and answers
12541    /// nothing rather than guessing.
12542    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12543        let mut matching = self
12544            .forwards
12545            .get(name)
12546            .into_iter()
12547            .flatten()
12548            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12549        let first = matching.next()?;
12550        matching
12551            .next()
12552            .is_none()
12553            .then(|| first.package_name.clone())
12554    }
12555}
12556
12557/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
12558/// debug build checks every answer against (and the one the parity tests drive
12559/// directly).  It walks the whole prefix per question, which is exactly the cost
12560/// #2754 removed from the release path.
12561#[cfg(any(debug_assertions, test))]
12562fn unique_earlier_cpp_namespace_forward<'tree>(
12563    recovered_node: Node<'tree>,
12564    name: &str,
12565    source: &str,
12566    ancestry: &ParentIndex<'tree>,
12567) -> Option<String> {
12568    let mut root = recovered_node;
12569    while let Some(parent) = ancestry.parent(root) {
12570        root = parent;
12571    }
12572
12573    let mut candidates = Vec::new();
12574    let mut stack = vec![root];
12575    while let Some(current) = stack.pop() {
12576        if current.start_byte() < recovered_node.start_byte()
12577            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12578            && forward.name == name
12579            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12580        {
12581            candidates.push(forward.package_name);
12582        }
12583
12584        let mut cursor = current.walk();
12585        for child in current.named_children(&mut cursor) {
12586            if child.start_byte() < recovered_node.start_byte() {
12587                stack.push(child);
12588            }
12589        }
12590    }
12591
12592    if candidates.len() == 1 {
12593        candidates.pop()
12594    } else {
12595        None
12596    }
12597}
12598
12599fn malformed_namespace_is_nearest_recovery_region(
12600    namespace_end_byte: usize,
12601    recovered_node: Node<'_>,
12602) -> bool {
12603    let mut root = recovered_node;
12604    while let Some(parent) = root.parent() {
12605        root = parent;
12606    }
12607    let mut cursor = root.walk();
12608    root.named_children(&mut cursor)
12609        .filter(|sibling| {
12610            namespace_end_byte <= sibling.start_byte()
12611                && sibling.end_byte() <= recovered_node.start_byte()
12612        })
12613        .all(is_malformed_namespace_recovery_trivia)
12614}
12615
12616fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12617    matches!(node.kind(), "ERROR" | "comment")
12618        || node.kind().starts_with("preproc_")
12619        || node.kind() == "expression_statement" && node.named_child_count() == 0
12620}
12621
12622/// Return the namespace path for a forward class only when the declaration is
12623/// at namespace scope.  A declaration nested in a function/class body may share
12624/// the same namespace ancestor but cannot identify a top-level class definition.
12625fn cpp_namespace_name_for_forward<'tree>(
12626    node: Node<'tree>,
12627    source: &str,
12628    ancestry: &ParentIndex<'tree>,
12629) -> Option<String> {
12630    cpp_namespace_definition_for_forward(node, ancestry)?;
12631    cpp_lexical_namespace_name(node, source)
12632}
12633
12634fn cpp_namespace_definition_for_forward<'tree>(
12635    node: Node<'tree>,
12636    ancestry: &ParentIndex<'tree>,
12637) -> Option<Node<'tree>> {
12638    let declaration = ancestry.parent(node)?;
12639    let mut ancestor = ancestry.parent(declaration);
12640    while let Some(current) = ancestor {
12641        if matches!(
12642            current.kind(),
12643            "compound_statement"
12644                | "field_declaration_list"
12645                | "class_specifier"
12646                | "struct_specifier"
12647                | "union_specifier"
12648                | "function_definition"
12649                | "lambda_expression"
12650        ) {
12651            return None;
12652        }
12653        if current.kind() == "namespace_definition" {
12654            return Some(current);
12655        }
12656        ancestor = ancestry.parent(current);
12657    }
12658    None
12659}
12660
12661fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12662    match node.kind() {
12663        "template_function" => node
12664            .child_by_field_name("name")
12665            .is_some_and(is_function_pointer_like_inner_declarator),
12666        _ => is_pointer_wrapper_declarator(node),
12667    }
12668}
12669
12670fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12671    match node.kind() {
12672        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12673        "parenthesized_declarator" => node
12674            .child_by_field_name("declarator")
12675            .or_else(|| last_named_child(node))
12676            .is_some_and(is_pointer_wrapper_declarator),
12677        _ => false,
12678    }
12679}
12680
12681/// The canonical owner chain and package of an out-of-line member whose
12682/// written qualifier starts with a name a `using ns::Type;` declaration bound
12683/// in the definition's own namespace scope (#6902 item 4).
12684///
12685/// `using imported::Type; void Type::method() {}` defines
12686/// `imported::Type::method`: the written `Type::` stays in the source, but the
12687/// member's identity follows the binding. The binding proves the owner only
12688/// when every visible binding of that name in this scope names the same
12689/// target. Conflicting bindings, a binding from an enclosing namespace (which
12690/// a same-named class in the inner namespace would shadow), and a using
12691/// declaration this walk cannot see (a preprocessor arm, another file) all
12692/// return `None`, and the caller keeps the written-qualifier reading.
12693fn using_bound_cpp_owner(
12694    declarator_name: Node<'_>,
12695    source: &str,
12696    scope: &ScopeInfo,
12697) -> Option<(Vec<String>, String)> {
12698    if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12699        return None;
12700    }
12701    let (components, explicitly_global) =
12702        structured_cpp_qualified_components(declarator_name, source)?;
12703    let (_, owner) = components.split_last()?;
12704    if explicitly_global
12705        || owner.is_empty()
12706        || components.iter().any(|component| component.is_template_id)
12707    {
12708        return None;
12709    }
12710    let mut targets = scope
12711        .visible_using_declarations
12712        .iter()
12713        .filter(|binding| {
12714            binding.scope_package == scope.package_name && binding.name == owner[0].name
12715        })
12716        .map(|binding| binding.target_package.as_str());
12717    let target_package = targets.next()?;
12718    if targets.any(|other| other != target_package) {
12719        return None;
12720    }
12721    Some((
12722        owner
12723            .iter()
12724            .map(|component| component.name.clone())
12725            .collect(),
12726        target_package.to_string(),
12727    ))
12728}
12729
12730fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12731    let cleaned = raw_name.trim_start_matches("template ").trim();
12732    // A leading `::` is the explicit-global marker, not an empty owner segment.
12733    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
12734    // erroneous macro envelope swallowing the first identifier of an
12735    // out-of-line `X::X` constructor, chromium #1573); without this strip the
12736    // split below yields owner_parts `[""]`, constructing a unit with an empty
12737    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
12738    let cleaned = cleaned.trim_start_matches("::");
12739    // Parser recovery can preserve two adjacent scope operators around a
12740    // missing component (for example `X::/**/::method` in compiler diagnostic
12741    // fixtures). Empty components are syntax-recovery artifacts, never C++
12742    // owners. Keeping one as the final owner constructed `short_name=".method"`
12743    // and violated the structured package/short boundary during a large LLVM
12744    // workspace build. This is the same legacy-string-to-FqName bridge as the
12745    // ordinary split above; discard only components that the delimiter itself
12746    // proves empty.
12747    let parts: Vec<_> = cleaned
12748        .split("::")
12749        .filter(|component| !component.is_empty())
12750        .collect();
12751    if parts.is_empty() {
12752        return (None, cleaned.to_string(), scope.package_name.clone());
12753    }
12754    if parts.len() > 1 {
12755        let name = parts.last().unwrap_or(&cleaned).to_string();
12756        let owner_parts = &parts[..parts.len() - 1];
12757        if let Some(class_unit) = &scope.class_unit {
12758            // Lexically inside a class body: the owner is that class, whatever
12759            // the declarator re-qualifies it as.
12760            return (
12761                Some(CppMemberOwner::Unit(class_unit.clone())),
12762                name,
12763                scope.package_name.clone(),
12764            );
12765        }
12766        if !scope.package_name.is_empty() {
12767            // Out-of-line member definition written *inside* an enclosing
12768            // `namespace {}` block (scope package is that namespace). Every
12769            // owner segment before the terminal member is a class-nesting step
12770            // -- an out-of-line nested-class member `Outer::Inner::method` in
12771            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
12772            // namespace path: `using namespace` never brings nested-class
12773            // access into unqualified scope, so C++ always writes the full
12774            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
12775            // that redundantly re-states the enclosing namespace it already
12776            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
12777            // strip that re-qualifying prefix (which duplicates a suffix of the
12778            // enclosing package path) before treating what remains as the
12779            // nested-class chain, so the redundant spelling still lands on the
12780            // same `log4cxx.Foo.method` identity as its header declaration.
12781            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12782            let owner = (!nested.is_empty()).then(|| {
12783                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12784            });
12785            return (owner, name, scope.package_name.clone());
12786        }
12787        // File scope (no enclosing `namespace {}` block, scope package empty).
12788        let (owner, package_name) = if owner_parts.len() > 1 {
12789            // A multi-segment qualifier at file scope with no enclosing
12790            // namespace: treat all but the last owner segment as the namespace
12791            // path and the last as the owning class (`ns1::ns2::Class::method`
12792            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
12793            // is really a namespace or an outer class cannot be told from the
12794            // declarator text alone here, and no enclosing namespace or
12795            // in-index owner is available at per-file extraction to confirm the
12796            // class reading, so the far-more-common namespace interpretation is
12797            // kept rather than guessed away (the nested-class-at-file-scope and
12798            // using-directive-qualified nested-class shapes remain on this
12799            // behavior; see #1121).
12800            (
12801                Some(CppMemberOwner::Chain(vec![
12802                    owner_parts.last().unwrap_or(&"").to_string(),
12803                ])),
12804                owner_parts[..owner_parts.len() - 1].join("::"),
12805            )
12806        } else {
12807            // A bare `Class::member` qualifier at file scope carries no
12808            // namespace segment of its own. The declarator alone cannot say
12809            // which namespace owns `Class` -- but a `using namespace X;`
12810            // directive already in effect at this point in the file (#1093,
12811            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
12812            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
12813            // is the remaining structural signal for it, so fall back to it
12814            // rather than leaving the definition's package empty while its
12815            // header declaration (parsed inside the `namespace {}` block) keeps
12816            // the real one -- an identity split that made the same member
12817            // unresolvable under its own displayed spelling.
12818            (
12819                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12820                cpp_using_directive_namespace_for_bare_owner(scope),
12821            )
12822        };
12823        return (owner, name, package_name);
12824    }
12825
12826    let package_name = scope.package_name.clone();
12827    let owner = scope
12828        .class_unit
12829        .as_ref()
12830        .map(|parent| CppMemberOwner::Unit(parent.clone()));
12831    (owner, cleaned.to_string(), package_name)
12832}
12833
12834/// Drop the leading owner segments of an out-of-line member qualifier that
12835/// merely re-state the enclosing namespace the definition already sits in, so
12836/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
12837/// definition may redundantly write `a::b::Outer::Inner::method` (or the
12838/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
12839/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
12840/// noise, not class-nesting steps. Returns the owner segments with the longest
12841/// such re-qualifying prefix removed (possibly all of them, when the qualifier
12842/// names only the enclosing namespace before the terminal member -- a
12843/// re-qualified free function). `package_name` is the enclosing namespace path
12844/// in its stored `::`-joined form; both sides are split on the same delimiter
12845/// the namespace walker joined them with, so this compares namespace *segments*
12846/// rather than scanning text.
12847fn strip_redundant_namespace_prefix<'a>(
12848    owner_parts: &'a [&'a str],
12849    package_name: &str,
12850) -> &'a [&'a str] {
12851    if package_name.is_empty() {
12852        return owner_parts;
12853    }
12854    let package_segments: Vec<&str> = package_name.split("::").collect();
12855    let max_prefix = owner_parts.len().min(package_segments.len());
12856    for prefix_len in (1..=max_prefix).rev() {
12857        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12858        if &owner_parts[..prefix_len] == package_suffix {
12859            return &owner_parts[prefix_len..];
12860        }
12861    }
12862    owner_parts
12863}
12864
12865/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
12866/// class name at file/namespace scope, from the `using namespace` directives
12867/// visible at this point in the file. Several may be in scope at once (a
12868/// primary `using namespace NS;` alongside deeper conveniences like `using
12869/// namespace NS::helpers;`); since the declarator gives no way to tell which
12870/// one actually declares the owner class, prefer the shallowest (fewest
12871/// `::`-separated segments) as the file's most likely "home" namespace,
12872/// breaking ties by declaration order. Returns an empty string (leaving the
12873/// caller's package unqualified, as before) when no using-namespace directive
12874/// is in scope.
12875fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12876    scope
12877        .visible_using_namespaces
12878        .iter()
12879        .min_by_key(|namespace| namespace.split("::").count())
12880        .cloned()
12881        .unwrap_or_default()
12882}
12883
12884struct CppQualifiedNameComponent {
12885    name: String,
12886    is_template_id: bool,
12887}
12888
12889/// Canonical nested-class chain for an out-of-line class definition written
12890/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
12891/// component per class (`["Outer", "Inner"]`).
12892///
12893/// The enclosing namespace fixes the namespace/class boundary: after an
12894/// optional redundant spelling of that namespace, every component belongs to
12895/// the class chain. File-scope qualified class names remain untouched because
12896/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
12897///
12898/// The components stay structured (rather than being `$`-joined here) so the
12899/// fq construction can push one Type/Nested segment per class; the `$`-joined
12900/// short-name display form is derived at the call sites that need it.
12901fn qualified_class_name_chain(
12902    class_node: Node<'_>,
12903    source: &str,
12904    scope: &ScopeInfo,
12905) -> Option<Vec<String>> {
12906    if scope.package_name.is_empty() || scope.class_unit.is_some() {
12907        return None;
12908    }
12909    let name = class_node.child_by_field_name("name")?;
12910    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12911    if explicitly_global
12912        || components.len() < 2
12913        || components.iter().any(|component| component.is_template_id)
12914    {
12915        return None;
12916    }
12917    let names = components
12918        .iter()
12919        .map(|component| component.name.as_str())
12920        .collect::<Vec<_>>();
12921    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12922    if class_chain.is_empty() {
12923        return None;
12924    }
12925    Some(class_chain.iter().map(|name| name.to_string()).collect())
12926}
12927
12928fn structured_cpp_qualified_components(
12929    qualified_name: Node<'_>,
12930    source: &str,
12931) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12932    if qualified_name.kind() != "qualified_identifier" {
12933        return None;
12934    }
12935
12936    let mut components = Vec::new();
12937    let mut current = qualified_name;
12938    let mut explicitly_global = false;
12939    loop {
12940        if current.kind() == "qualified_identifier" {
12941            if let Some(component) = current.child_by_field_name("scope") {
12942                components.push(canonical_cpp_qualified_component(component, source)?);
12943            } else if components.is_empty() {
12944                explicitly_global = true;
12945            } else {
12946                return None;
12947            }
12948            current = current.child_by_field_name("name")?;
12949        } else {
12950            components.push(canonical_cpp_qualified_component(current, source)?);
12951            break;
12952        }
12953    }
12954    Some((components, explicitly_global))
12955}
12956
12957fn split_structured_templated_cpp_name(
12958    declarator: Node<'_>,
12959    declarator_name: Node<'_>,
12960    source: &str,
12961    scope: &ScopeInfo,
12962) -> Option<(Option<CppMemberOwner>, String, String)> {
12963    let (mut components, explicitly_global) =
12964        structured_cpp_qualified_components(declarator_name, source)?;
12965
12966    let terminal = components.pop()?;
12967    let owner_start = components
12968        .iter()
12969        .position(|component| component.is_template_id)?;
12970    let explicit_package = components[..owner_start]
12971        .iter()
12972        .map(|component| component.name.as_str())
12973        .collect::<Vec<_>>()
12974        .join("::");
12975    let explicit_package_is_empty = explicit_package.is_empty();
12976    let package_name = match (
12977        explicitly_global,
12978        scope.package_name.is_empty(),
12979        explicit_package_is_empty,
12980    ) {
12981        (true, _, _) => explicit_package,
12982        (false, _, true) => scope.package_name.clone(),
12983        (false, true, false) => explicit_package,
12984        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12985    };
12986    // Same identity-split fallback as `split_cpp_name` (#1093): a template
12987    // specialization's owner class named with no namespace segment of its own
12988    // (`explicit_package` empty) at file scope (`explicitly_global` false)
12989    // with nothing enclosing (`package_name` still empty) has no structural
12990    // signal for its namespace besides an in-scope `using namespace X;`.
12991    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12992    {
12993        cpp_using_directive_namespace_for_bare_owner(scope)
12994    } else {
12995        package_name
12996    };
12997    // A specialization member keeps its specialization owner (`Box<T*>`)
12998    // rather than collapsing into the primary's; a qualifier the template
12999    // levels cannot read keeps the bare names.
13000    let owner_chain = match cpp_out_of_line_owner_names(declarator, source)
13001        .filter(|names| names.len() == components.len())
13002    {
13003        Some(mut names) => names.split_off(owner_start),
13004        None => components[owner_start..]
13005            .iter()
13006            .map(|component| component.name.clone())
13007            .collect::<Vec<_>>(),
13008    };
13009    if owner_chain.is_empty() || terminal.name.is_empty() {
13010        return None;
13011    }
13012
13013    Some((
13014        Some(CppMemberOwner::Chain(owner_chain)),
13015        terminal.name,
13016        package_name,
13017    ))
13018}
13019
13020fn canonical_cpp_qualified_component(
13021    mut component: Node<'_>,
13022    source: &str,
13023) -> Option<CppQualifiedNameComponent> {
13024    let mut is_template_id = false;
13025    loop {
13026        match component.kind() {
13027            "template_type" => {
13028                is_template_id = true;
13029                component = component.child_by_field_name("name")?;
13030            }
13031            "dependent_name" => component = component.named_child(0)?,
13032            "identifier"
13033            | "field_identifier"
13034            | "namespace_identifier"
13035            | "type_identifier"
13036            | "operator_name"
13037            | "destructor_name" => {
13038                let name = normalize_cpp_whitespace(node_text(component, source));
13039                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
13040                    name,
13041                    is_template_id,
13042                });
13043            }
13044            _ => component = component.child_by_field_name("name")?,
13045        }
13046    }
13047}
13048
13049fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
13050    if let Some(name) = macro_decorated_unqualified_name(node) {
13051        return extract_declarator_name(name, source);
13052    }
13053    match node.kind() {
13054        "identifier"
13055        | "field_identifier"
13056        | "type_identifier"
13057        | "operator_name"
13058        | "destructor_name"
13059        | "qualified_identifier" => node_text(node, source).to_string(),
13060        "function_declarator"
13061        | "pointer_declarator"
13062        | "reference_declarator"
13063        | "parenthesized_declarator"
13064        | "array_declarator"
13065        | "template_function" => node
13066            .child_by_field_name("declarator")
13067            .or_else(|| node.child_by_field_name("name"))
13068            .or_else(|| last_named_child(node))
13069            .map(|child| extract_declarator_name(child, source))
13070            .unwrap_or_else(|| node_text(node, source).to_string()),
13071        _ => node
13072            .child_by_field_name("name")
13073            .map(|child| extract_declarator_name(child, source))
13074            .unwrap_or_else(|| node_text(node, source).to_string()),
13075    }
13076}
13077
13078/// Extract a callable identity only through declaration-shaped AST nodes.
13079/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
13080/// expose the call's parameter list as a false function declarator; accepting
13081/// arbitrary node text there emitted bogus names such as `.*f`.
13082fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13083    if let Some(name) = macro_decorated_unqualified_name(node) {
13084        return extract_callable_declarator_name(name, source);
13085    }
13086    match node.kind() {
13087        "identifier"
13088        | "field_identifier"
13089        | "type_identifier"
13090        | "operator_name"
13091        | "destructor_name"
13092        | "qualified_identifier" => Some(node_text(node, source).to_string()),
13093        "function_declarator"
13094        | "pointer_declarator"
13095        | "reference_declarator"
13096        | "parenthesized_declarator"
13097        | "array_declarator"
13098        | "template_function" => node
13099            .child_by_field_name("declarator")
13100            .or_else(|| node.child_by_field_name("name"))
13101            .and_then(|child| extract_callable_declarator_name(child, source)),
13102        _ => None,
13103    }
13104}
13105
13106fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
13107    loop {
13108        if let Some(name) = macro_decorated_unqualified_name(node) {
13109            node = name;
13110            continue;
13111        }
13112        match node.kind() {
13113            "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
13114                let name = node_text(node, source).trim().to_string();
13115                return (!name.is_empty()).then_some(name);
13116            }
13117            _ => {
13118                node = node
13119                    .child_by_field_name("declarator")
13120                    .or_else(|| node.child_by_field_name("name"))
13121                    .or_else(|| last_named_child(node))?;
13122            }
13123        }
13124    }
13125}
13126
13127/// Recover a C field whose name is wrapped in the function-like
13128/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
13129/// function declarator, while retaining its argument as a structured child.
13130/// The exact macro name and one-argument shape keep ordinary function-pointer
13131/// fields and unknown malformed declarators fail-closed.
13132#[derive(Clone, Copy)]
13133pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13134    pub(crate) name: Node<'tree>,
13135    pub(crate) declarator: Node<'tree>,
13136}
13137
13138impl RecoveredFunctionLikeFieldDeclarator<'_> {
13139    pub(crate) fn pointer_depth(self) -> i32 {
13140        let mut depth = 0;
13141        let mut current = self.declarator;
13142        while current.kind() != "function_declarator" {
13143            if current.kind() == "pointer_declarator" {
13144                depth += 1;
13145            }
13146            current = current
13147                .child_by_field_name("declarator")
13148                .expect("recovered field wrapper has an inner declarator");
13149        }
13150        depth
13151    }
13152}
13153
13154/// Whether a class-scope field declaration is really a function-like macro
13155/// invocation whose replacement the parser never saw.
13156///
13157/// The invocation carries no terminator of its own, so tree-sitter invents a
13158/// zero-width `;` and reads the macro name as the member's type and the
13159/// argument list as a `parenthesized_declarator`. That shape is not a member:
13160/// a member terminates itself, and with a real terminator the grammar reads
13161/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
13162/// type can name the macro being invoked; a builtin type keeps whatever
13163/// recovery the ordinary declarator walk makes of it.
13164fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13165    if node.kind() != "field_declaration" {
13166        return false;
13167    }
13168    let Some(type_node) = node.child_by_field_name("type") else {
13169        return false;
13170    };
13171    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13172        return false;
13173    }
13174    let Some(declarator) = node.child_by_field_name("declarator") else {
13175        return false;
13176    };
13177    if declarator.kind() != "parenthesized_declarator" {
13178        return false;
13179    }
13180    let mut invented_terminator = false;
13181    for child in children_iter(node) {
13182        if child.kind() != ";" {
13183            continue;
13184        }
13185        if !child.is_missing() {
13186            return false;
13187        }
13188        invented_terminator = true;
13189    }
13190    invented_terminator
13191}
13192
13193/// An access label that a class-scope function-like macro invocation
13194/// swallowed, as [`cpp_macro_swallowed_access_label`] reads it.
13195pub(crate) struct CppSwallowedAccessLabel<'tree> {
13196    /// The access keyword, which the grammar read as a `field_identifier`.
13197    pub(crate) keyword: Node<'tree>,
13198    /// `"public"`, `"protected"` or `"private"`.
13199    pub(crate) access: &'static str,
13200}
13201
13202/// Whether a class-body member is a function-like macro invocation that
13203/// swallowed the access label written after it, and with it the members under
13204/// that label.
13205///
13206/// `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)` carries no terminator of its own
13207/// (#3298). The macro-declaration grammar (brokk-tree-sitter-cpp 0.24.4) reads
13208/// the invocation as a `macro_type_specifier`, the `public` after it as the
13209/// member's `field_identifier` declarator, and the label's `:` as a bitfield
13210/// whose width expression shreds the next member, so the constructor written
13211/// under the label is left with no declarator at all. An access keyword is
13212/// never a declarator name, and a bitfield after it is how the parser spent
13213/// the label's colon, so the shape is the evidence. The access ladders read
13214/// the label from it too, because it still applies to every member after it.
13215pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13216    node: Node<'tree>,
13217    source: &str,
13218) -> Option<CppSwallowedAccessLabel<'tree>> {
13219    if !matches!(node.kind(), "field_declaration" | "function_definition") {
13220        return None;
13221    }
13222    node.child_by_field_name("type")
13223        .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13224    let keyword = node
13225        .child_by_field_name("declarator")
13226        .filter(|declarator| declarator.kind() == "field_identifier")?;
13227    let access = match node_text(keyword, source) {
13228        "public" => "public",
13229        "protected" => "protected",
13230        "private" => "private",
13231        _ => return None,
13232    };
13233    let mut clause = keyword.next_sibling()?;
13234    if clause.kind() == "ERROR" {
13235        clause = clause.child(0)?;
13236    }
13237    if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13238        return None;
13239    }
13240    Some(CppSwallowedAccessLabel { keyword, access })
13241}
13242
13243pub(crate) fn recovered_function_like_field_declarator<'tree>(
13244    node: Node<'tree>,
13245    source: &str,
13246) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13247    if node.kind() != "field_declaration" {
13248        return None;
13249    }
13250    let outer_declarator = node.child_by_field_name("declarator")?;
13251    let mut declarator = outer_declarator;
13252    while matches!(
13253        declarator.kind(),
13254        "pointer_declarator"
13255            | "reference_declarator"
13256            | "array_declarator"
13257            | "parenthesized_declarator"
13258    ) {
13259        declarator = declarator.child_by_field_name("declarator")?;
13260    }
13261    if declarator.kind() != "function_declarator" {
13262        return None;
13263    }
13264    let macro_name = declarator.child_by_field_name("declarator")?;
13265    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13266    {
13267        return None;
13268    }
13269    let parameters = declarator.child_by_field_name("parameters")?;
13270    let mut cursor = parameters.walk();
13271    let mut arguments = parameters.named_children(&mut cursor);
13272    let parameter = arguments.next()?;
13273    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13274        return None;
13275    }
13276    let name = parameter.child_by_field_name("type").filter(|argument| {
13277        matches!(
13278            argument.kind(),
13279            "identifier" | "field_identifier" | "type_identifier"
13280        )
13281    })?;
13282    Some(RecoveredFunctionLikeFieldDeclarator {
13283        name,
13284        declarator: outer_declarator,
13285    })
13286}
13287
13288use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13289
13290fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13291    let name_node = node.child_by_field_name("name")?;
13292    let name = normalize_cpp_whitespace(node_text(name_node, source));
13293    (!name.is_empty()).then_some(name)
13294}
13295
13296fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13297    if node.kind() != "declaration" {
13298        return Vec::new();
13299    }
13300    // The `typedef`/`using` keyword: a `type_identifier` in the type field,
13301    // or -- under the macro-declaration grammar, which reads the keyword as
13302    // an object-like macro -- the identifier a leading `macro_modifier`
13303    // wraps, with the aliased type in the type field.
13304    let keyword_is_alias = node
13305        .child_by_field_name("type")
13306        .filter(|node| {
13307            node.kind() == "type_identifier"
13308                && matches!(node_text(*node, source), "using" | "typedef")
13309        })
13310        .map(|keyword| node_text(keyword, source).to_owned())
13311        .or_else(|| {
13312            node.named_children(&mut node.walk())
13313                .find_map(macro_modifier_identifier)
13314                .map(|identifier| node_text(identifier, source).to_owned())
13315                .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13316        });
13317    let Some(keyword_is_alias) = keyword_is_alias else {
13318        return Vec::new();
13319    };
13320    let Some(declarator) = node.child_by_field_name("declarator") else {
13321        return Vec::new();
13322    };
13323    if keyword_is_alias == "using"
13324        && (declarator.kind() != "init_declarator"
13325            || declarator.child_by_field_name("value").is_none())
13326    {
13327        return Vec::new();
13328    }
13329    if keyword_is_alias == "typedef"
13330        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13331    {
13332        return vec![alias_name];
13333    }
13334    extract_typedef_declarator_name(declarator, source)
13335        .into_iter()
13336        .collect()
13337}
13338
13339/// The RHS retained by the same malformed alias shapes declaration admission
13340/// accepts. The alias name proves which declarator owns this source evidence.
13341pub(crate) fn recovered_alias_type_node<'tree>(
13342    node: Node<'tree>,
13343    alias_name: &str,
13344    source: &str,
13345) -> Option<Node<'tree>> {
13346    if node.kind() != "declaration" {
13347        return None;
13348    }
13349    let declarator = node.child_by_field_name("declarator")?;
13350    let admitted = recovered_type_alias_names(node, source)
13351        .iter()
13352        .any(|name| name == alias_name)
13353        || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13354    if !admitted {
13355        return None;
13356    }
13357    if declarator.kind() == "init_declarator" {
13358        return declarator.child_by_field_name("value");
13359    }
13360    // The macro-declaration grammar keeps the aliased type in the type field
13361    // and the keyword as a `macro_modifier`; the alias name is the
13362    // declarator, so the type field is the RHS evidence.
13363    if node
13364        .named_children(&mut node.walk())
13365        .find_map(macro_modifier_identifier)
13366        .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13367    {
13368        return node.child_by_field_name("type");
13369    }
13370    (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13371        .then_some(declarator)
13372}
13373
13374fn recovered_typedef_error_alias_name(
13375    declaration: Node<'_>,
13376    declarator: Node<'_>,
13377    source: &str,
13378) -> Option<String> {
13379    // An export macro between `class` and its name can make tree-sitter parse
13380    // the recovered class body as a function body. In that shape,
13381    //
13382    //     typedef spi::Filter BASE_CLASS;
13383    //
13384    // becomes a declaration whose `declarator` is the underlying qualified
13385    // type (`spi::Filter`) and whose actual alias name is displaced into the
13386    // following ERROR node. Do not publish the terminal underlying type
13387    // (`Filter`) as a false class-owned alias.
13388    if declarator.kind() != "qualified_identifier" {
13389        return None;
13390    }
13391    let mut cursor = declaration.walk();
13392    let mut errors = declaration
13393        .named_children(&mut cursor)
13394        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13395    let error = errors.next()?;
13396    if errors.next().is_some() || error.named_child_count() != 1 {
13397        return None;
13398    }
13399    let name = error.named_child(0)?;
13400    if !matches!(
13401        name.kind(),
13402        "identifier" | "field_identifier" | "type_identifier"
13403    ) {
13404        return None;
13405    }
13406    let name = normalize_cpp_whitespace(node_text(name, source));
13407    (!name.is_empty()).then_some(name)
13408}
13409
13410fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13411    // A function-like token in the type position can make tree-sitter expose
13412    // its argument as a parenthesized declarator. Do not publish that argument
13413    // as an alias. The macro-specific recovery below handles the proven shape.
13414    if fragmented_parenthesized_typedef_type(node).is_some() {
13415        return Vec::new();
13416    }
13417    let has_function_like_macro_type = node
13418        .child_by_field_name("type")
13419        .filter(|type_node| type_node.kind() == "type_identifier")
13420        .is_some_and(|type_node| {
13421            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13422        });
13423    let mut names = Vec::new();
13424    let mut cursor = node.walk();
13425    for declarator in node.children_by_field_name("declarator", &mut cursor) {
13426        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13427            continue;
13428        }
13429        if let Some(name) = extract_typedef_declarator_name(declarator, source)
13430            && !names.contains(&name)
13431        {
13432            names.push(name);
13433        }
13434    }
13435    names
13436}
13437
13438struct RecoveredMacroTypedefAlias<'tree> {
13439    name: String,
13440    end_node: Node<'tree>,
13441}
13442
13443/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
13444/// into an identifier expression statement. The uppercase macro token, missing
13445/// typedef terminator, and complete sibling terminator prove this exact shape.
13446fn recovered_macro_typedef_alias<'tree>(
13447    node: Node<'tree>,
13448    source: &str,
13449) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13450    let type_node = fragmented_parenthesized_typedef_type(node)?;
13451    if type_node.kind() != "type_identifier"
13452        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13453    {
13454        return None;
13455    }
13456
13457    let end_node = node.next_named_sibling()?;
13458    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13459        return None;
13460    }
13461    let name_node = end_node.named_child(0)?;
13462    if name_node.kind() != "identifier" {
13463        return None;
13464    }
13465    let has_terminator = (0..end_node.child_count()).any(|index| {
13466        end_node
13467            .child(index)
13468            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13469    });
13470    if !has_terminator {
13471        return None;
13472    }
13473    let name = normalize_cpp_whitespace(node_text(name_node, source));
13474    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13475}
13476
13477fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13478    if node.kind() != "type_definition" {
13479        return None;
13480    }
13481    let mut declarator_cursor = node.walk();
13482    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13483    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13484        return None;
13485    }
13486    let has_missing_terminator = (0..node.child_count()).any(|index| {
13487        node.child(index)
13488            .is_some_and(|child| child.kind() == ";" && child.is_missing())
13489    });
13490    if !has_missing_terminator {
13491        return None;
13492    }
13493    node.child_by_field_name("type")
13494}
13495
13496pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13497    let mut stack = vec![node];
13498    while let Some(node) = stack.pop() {
13499        let child = match node.kind() {
13500            "identifier" | "field_identifier" | "type_identifier" => {
13501                return direct_identifier_name(node, source);
13502            }
13503            "qualified_identifier" => node.child_by_field_name("name")?,
13504            _ => node
13505                .child_by_field_name("declarator")
13506                .or_else(|| node.child_by_field_name("name"))
13507                .or_else(|| last_named_child(node))?,
13508        };
13509        stack.push(child);
13510    }
13511    None
13512}
13513
13514fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13515    if let Some(name_node) = node.child_by_field_name("name") {
13516        let name = normalize_cpp_whitespace(node_text(name_node, source));
13517        return (!name.is_empty()).then_some(name);
13518    }
13519    let mut cursor = node.walk();
13520    node.named_children(&mut cursor)
13521        .find(|child| {
13522            matches!(
13523                child.kind(),
13524                "identifier" | "field_identifier" | "type_identifier"
13525            )
13526        })
13527        .and_then(|name_node| direct_identifier_name(name_node, source))
13528}
13529
13530fn render_cpp_type_signature(
13531    node: Node<'_>,
13532    source: &str,
13533    template_signature: Option<&str>,
13534) -> String {
13535    let text = normalize_cpp_whitespace(node_text(node, source));
13536    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13537    let rendered = if head.ends_with(';') {
13538        head.to_string()
13539    } else {
13540        format!("{head} {{")
13541    };
13542    if let Some(template_signature) = template_signature {
13543        format!("template {template_signature} {rendered}")
13544    } else {
13545        rendered
13546    }
13547}
13548
13549fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13550    if let Some(recovered) = recovered_pyobject_head_field(node, source)
13551        && recovered.declarator == declarator
13552    {
13553        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13554        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13555        return format!("{type_text} {declarator};");
13556    }
13557    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13558        && recovered.name == declarator
13559    {
13560        let type_text = node
13561            .child_by_field_name("type")
13562            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13563            .unwrap_or_default();
13564        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13565        let mut prefix = String::new();
13566        let mut suffix = String::new();
13567        let mut current = recovered.declarator;
13568        while current.kind() != "function_declarator" {
13569            match current.kind() {
13570                "pointer_declarator" => prefix.push('*'),
13571                "reference_declarator" => prefix.push('&'),
13572                "array_declarator" => {
13573                    let size = current
13574                        .child_by_field_name("size")
13575                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13576                        .unwrap_or_default();
13577                    suffix.push('[');
13578                    suffix.push_str(&size);
13579                    suffix.push(']');
13580                }
13581                "parenthesized_declarator" => {}
13582                _ => unreachable!("validated recovered field declarator wrapper"),
13583            }
13584            current = current
13585                .child_by_field_name("declarator")
13586                .expect("recovered field wrapper has an inner declarator");
13587        }
13588        let separator = if prefix.is_empty() { "" } else { " " };
13589        return format!("{type_text} {prefix}{separator}{name}{suffix};");
13590    }
13591    if let Some(signature) =
13592        render_recovered_macro_qualified_field_signature(node, declarator, source)
13593    {
13594        return signature;
13595    }
13596    let prefix = cpp_declaration_prefix(node, source);
13597    let name = extract_variable_name(declarator, source).unwrap_or_default();
13598    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13599    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13600        || (prefix.ends_with('&') && raw_suffix == "&")
13601    {
13602        String::new()
13603    } else {
13604        raw_suffix
13605    };
13606
13607    let mut rendered = if suffix.is_empty() {
13608        format!("{prefix} {name}")
13609    } else if suffix.starts_with('*') || suffix.starts_with('&') {
13610        format!("{prefix}{suffix} {name}")
13611    } else if suffix.starts_with('[') || suffix.starts_with('(') {
13612        format!("{prefix} {name}{suffix}")
13613    } else {
13614        format!("{prefix} {suffix}{name}")
13615    };
13616    rendered = collapse_cpp_whitespace(&rendered);
13617
13618    // Check the terminal token directly instead of normalizing the whole
13619    // declaration, which can copy a large initializer for each field.
13620    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13621        format!("{rendered} = {initializer};")
13622    } else if cpp_direct_semicolon(node).is_some() {
13623        format!("{rendered};")
13624    } else {
13625        rendered
13626    }
13627}
13628
13629fn render_recovered_macro_qualified_field_signature(
13630    node: Node<'_>,
13631    declarator: Node<'_>,
13632    source: &str,
13633) -> Option<String> {
13634    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13635    if !recovered
13636        .iter()
13637        .any(|candidate| same_node(*candidate, declarator))
13638    {
13639        return None;
13640    }
13641    let pseudo_declarator = node.child_by_field_name("declarator")?;
13642    let mut cursor = node.walk();
13643    let clause = node
13644        .named_children(&mut cursor)
13645        .find(|child| child.kind() == "bitfield_clause")?;
13646    let mut cursor = clause.walk();
13647    let error = clause
13648        .named_children(&mut cursor)
13649        .find(|child| child.kind() == "ERROR")?;
13650    let qualified_type =
13651        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13652    let prefix = cpp_declaration_prefix(node, source);
13653    let name = extract_variable_name(declarator, source)?;
13654    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13655    let mut rendered = if suffix.is_empty() {
13656        format!("{prefix} {qualified_type} {name}")
13657    } else {
13658        format!("{prefix} {qualified_type} {suffix} {name}")
13659    };
13660    rendered = collapse_cpp_whitespace(&rendered);
13661
13662    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13663        Some(format!(
13664            "{rendered} = {};",
13665            normalize_cpp_whitespace(node_text(initializer, source))
13666        ))
13667    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13668        Some(format!("{rendered} = {initializer};"))
13669    } else {
13670        Some(format!("{rendered};"))
13671    }
13672}
13673
13674fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13675    match node.kind() {
13676        "pointer_expression" => {
13677            let operator = node
13678                .child_by_field_name("operator")
13679                .or_else(|| node.child(0))
13680                .map(|operator| node_text(operator, source))
13681                .unwrap_or("*");
13682            let argument = node
13683                .child_by_field_name("argument")
13684                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13685                .unwrap_or_default();
13686            format!("{operator}{argument}")
13687        }
13688        "unary_expression" => {
13689            let operator = node
13690                .child_by_field_name("operator")
13691                .or_else(|| node.child(0))
13692                .map(|operator| node_text(operator, source))
13693                .unwrap_or_default();
13694            let argument = node
13695                .child_by_field_name("argument")
13696                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13697                .unwrap_or_default();
13698            format!("{operator}{argument}")
13699        }
13700        "identifier" | "field_identifier" => String::new(),
13701        _ => cpp_declarator_suffix_without_name(node, source),
13702    }
13703}
13704
13705fn recovered_macro_qualified_field_initializer<'tree>(
13706    clause: Node<'tree>,
13707    declarator: Node<'tree>,
13708) -> Option<Node<'tree>> {
13709    let mut stack = vec![clause];
13710    while let Some(current) = stack.pop() {
13711        if current.kind() == "assignment_expression"
13712            && current
13713                .child_by_field_name("left")
13714                .is_some_and(|left| same_node(left, declarator))
13715        {
13716            return current.child_by_field_name("right");
13717        }
13718        let mut cursor = current.walk();
13719        stack.extend(current.named_children(&mut cursor));
13720    }
13721    None
13722}
13723
13724fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13725    let text = node_text(node, source);
13726    let mut cursor = node.walk();
13727    let first_declarator = node.named_children(&mut cursor).find(|child| {
13728        matches!(
13729            child.kind(),
13730            "init_declarator"
13731                | "identifier"
13732                | "field_identifier"
13733                | "pointer_declarator"
13734                | "reference_declarator"
13735                | "array_declarator"
13736                | "function_declarator"
13737        )
13738    });
13739    let prefix = if let Some(first_declarator) = first_declarator {
13740        let end = first_declarator
13741            .start_byte()
13742            .saturating_sub(node.start_byte());
13743        let mut prefix = text.get(..end).unwrap_or(text).to_string();
13744        let declarator_suffix = match first_declarator.kind() {
13745            "init_declarator" => first_declarator
13746                .child_by_field_name("declarator")
13747                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13748                .unwrap_or_default(),
13749            _ => cpp_declarator_suffix_without_name(first_declarator, source),
13750        };
13751        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13752            prefix.push_str(&declarator_suffix);
13753        }
13754        return collapse_cpp_whitespace(&prefix)
13755            .trim_end_matches(',')
13756            .trim_end_matches(';')
13757            .trim()
13758            .to_string();
13759    } else {
13760        text
13761    };
13762    collapse_cpp_whitespace(prefix)
13763        .trim_end_matches(',')
13764        .trim_end_matches(';')
13765        .trim()
13766        .to_string()
13767}
13768
13769fn cpp_preserved_initializer(
13770    declaration_node: Node<'_>,
13771    declarator: Node<'_>,
13772    source: &str,
13773) -> Option<String> {
13774    let name = extract_variable_name(declarator, source)?;
13775    let mut cursor = declaration_node.walk();
13776    for child in declaration_node.named_children(&mut cursor) {
13777        if child.kind() != "init_declarator" {
13778            continue;
13779        }
13780        let Some(inner) = child.child_by_field_name("declarator") else {
13781            continue;
13782        };
13783        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13784            continue;
13785        }
13786        let value = child.child_by_field_name("value")?;
13787        let kind = value.kind();
13788        if matches!(
13789            kind,
13790            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13791        ) {
13792            return Some(normalize_cpp_whitespace(node_text(value, source)));
13793        }
13794        break;
13795    }
13796    // The current grammar keeps a member initializer as a `default_value`
13797    // field beside the declarator rather than inside an `init_declarator`.
13798    if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13799        && let Some(value) = equals.next_sibling().filter(|node| {
13800            declaration_node
13801                .child_by_field_name("default_value")
13802                .is_some_and(|default| same_node(default, *node))
13803        })
13804    {
13805        let kind = value.kind();
13806        if matches!(
13807            kind,
13808            "number_literal"
13809                | "float_literal"
13810                | "char_literal"
13811                | "true"
13812                | "false"
13813                | "null"
13814                | "nullptr"
13815                | "identifier"
13816        ) {
13817            return Some(normalize_cpp_whitespace(node_text(value, source)));
13818        }
13819    }
13820    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13821    let pattern = format!(
13822        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13823        regex::escape(&name)
13824    );
13825    Regex::new(&pattern)
13826        .ok()
13827        .and_then(|regex| regex.captures(&declaration_text))
13828        .and_then(|captures| captures.get(1))
13829        .map(|value| value.as_str().to_string())
13830}
13831
13832fn render_cpp_function_display_signature_from_node<'tree>(
13833    node: Node<'tree>,
13834    source: &str,
13835    template_signature: Option<&str>,
13836    has_body: bool,
13837    ancestry: &ParentIndex<'tree>,
13838) -> String {
13839    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13840    let parent_text = node_text(root, source);
13841    let body_local_start = root
13842        .child_by_field_name("body")
13843        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13844        .unwrap_or(parent_text.len());
13845    let display = parent_text
13846        .get(..body_local_start)
13847        .unwrap_or(parent_text)
13848        .trim()
13849        .trim();
13850    let display = if let Some(template_signature) = template_signature {
13851        if display.starts_with("template ") {
13852            display.to_string()
13853        } else {
13854            format!("template {template_signature} {display}")
13855        }
13856    } else {
13857        display.to_string()
13858    };
13859    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13860    if has_body {
13861        format!("{display} {{...}}")
13862    } else {
13863        format!("{display};")
13864    }
13865}
13866
13867fn cpp_template_signature(
13868    template_node: Node<'_>,
13869    declaration_child: Node<'_>,
13870    source: &str,
13871) -> Option<String> {
13872    let text = source
13873        .get(template_node.start_byte()..declaration_child.start_byte())
13874        .unwrap_or("");
13875    let text = normalize_cpp_whitespace(text);
13876    let start = text.find('<')?;
13877    let end = text.rfind('>')?;
13878    if end < start {
13879        return None;
13880    }
13881    Some(text[start..=end].to_string())
13882}
13883
13884struct RecoveredFragmentedPartialSpecialization<'tree> {
13885    declaration_node: Node<'tree>,
13886    name: String,
13887    range: Range,
13888    prefix_members: Vec<Node<'tree>>,
13889    member_siblings: Vec<Node<'tree>>,
13890    following_declarations: Vec<Node<'tree>>,
13891}
13892
13893struct RecoveredFragmentedPreprocessorClass<'tree> {
13894    declaration_node: Node<'tree>,
13895    class_node: Node<'tree>,
13896    body: Node<'tree>,
13897    name: String,
13898    range: Range,
13899    tail_members: Vec<Node<'tree>>,
13900    member_siblings: Vec<Node<'tree>>,
13901}
13902
13903/// Recover a class whose preprocessor-fragmented parse closes at an early
13904/// member body and publishes the remaining in-class declarations as siblings
13905/// of the surrounding alternative. Primary classes are admitted only when an
13906/// earlier branch contains the matching bodyless declaration and the class
13907/// node retains the displaced `#endif`. Partial specializations instead carry
13908/// their identity structurally in the `template_type` name and template
13909/// metadata. Retain the original AST nodes and re-own only the siblings through
13910/// the displaced structural `};` terminator.
13911fn recover_fragmented_preprocessor_class<'tree>(
13912    template_node: Node<'tree>,
13913    source: &str,
13914    ancestry: &ParentIndex<'tree>,
13915) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13916    let alternative = ancestry.parent(template_node)?;
13917    if alternative.kind() != "preproc_else" {
13918        return None;
13919    }
13920    let conditional = alternative.parent()?;
13921    if conditional.kind() != "preproc_if" {
13922        return None;
13923    }
13924    let declaration_node = template_node
13925        .named_children(&mut template_node.walk())
13926        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13927    let class_node = declaration_node
13928        .named_children(&mut declaration_node.walk())
13929        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13930    let body = cpp_body_node(class_node)?;
13931    if class_node.end_byte() >= declaration_node.end_byte() {
13932        return None;
13933    }
13934    let name = class_like_name(class_node, source, ancestry)?;
13935    let is_partial_specialization = class_node
13936        .child_by_field_name("name")
13937        .is_some_and(|class_name| class_name.kind() == "template_type");
13938    if is_partial_specialization {
13939        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13940        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13941            return None;
13942        }
13943    } else {
13944        if !class_has_displaced_preprocessor_terminator(class_node) {
13945            return None;
13946        }
13947        let matching_other_branch = conditional
13948            .named_children(&mut conditional.walk())
13949            .take_while(|child| !same_node(*child, alternative))
13950            .filter(|child| child.kind() == "template_declaration")
13951            .filter_map(first_class_like_child)
13952            .any(|candidate| {
13953                cpp_body_node(candidate).is_none()
13954                    && class_like_name(candidate, source, ancestry).as_deref()
13955                        == Some(name.as_str())
13956            });
13957        if !matching_other_branch {
13958            return None;
13959        }
13960    }
13961
13962    let mut tail_members = Vec::new();
13963    let mut saw_class = false;
13964    let mut declaration_cursor = declaration_node.walk();
13965    for child in declaration_node.named_children(&mut declaration_cursor) {
13966        if same_node(child, class_node) {
13967            saw_class = true;
13968        } else if saw_class {
13969            tail_members.push(child);
13970        }
13971    }
13972
13973    let mut member_siblings = Vec::new();
13974    let mut saw_template = false;
13975    let mut terminator = None;
13976    for index in 0..alternative.child_count() {
13977        let Some(child) = alternative.child(index) else {
13978            continue;
13979        };
13980        if same_node(child, template_node) {
13981            saw_template = true;
13982            continue;
13983        }
13984        if !saw_template {
13985            continue;
13986        }
13987        if displaced_fragmented_class_terminator(alternative, index) {
13988            terminator = alternative.child(index + 1);
13989            break;
13990        }
13991        if child.is_named() {
13992            member_siblings.push(child);
13993        }
13994    }
13995    let terminator = terminator?;
13996    Some(RecoveredFragmentedPreprocessorClass {
13997        declaration_node,
13998        class_node,
13999        body,
14000        name,
14001        range: Range {
14002            start_byte: class_node.start_byte(),
14003            end_byte: terminator.end_byte(),
14004            start_line: class_node.start_position().row + 1,
14005            end_line: terminator.end_position().row + 1,
14006        },
14007        tail_members,
14008        member_siblings,
14009    })
14010}
14011
14012fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
14013    (0..class_node.child_count()).any(|index| {
14014        class_node.child(index).is_some_and(|child| {
14015            child.kind() == "ERROR"
14016                && (0..child.child_count()).any(|error_index| {
14017                    child
14018                        .child(error_index)
14019                        .is_some_and(|token| token.kind() == "#endif")
14020                })
14021        })
14022    })
14023}
14024
14025/// The real `#endif` that tree-sitter consumed inside an error subtree.
14026///
14027/// A preprocessor directive inside a malformed array bound can cause later
14028/// declarations to remain children of the conditional. The non-missing token
14029/// still gives the exact structured boundary. Ignore nested conditionals and
14030/// select the last unpaired error-owned token. Tree-sitter can pair a later
14031/// outer `#endif` with this conditional, so the direct terminator is not
14032/// necessarily missing.
14033pub fn cpp_displaced_preprocessor_terminator<'tree>(
14034    conditional: Node<'tree>,
14035) -> Option<Node<'tree>> {
14036    if !conditional.has_error() {
14037        return None;
14038    }
14039    let has_concrete_direct_terminator = conditional
14040        .child_count()
14041        .checked_sub(1)
14042        .and_then(|index| conditional.child(index))
14043        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
14044    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
14045        // A structured alternative proves that the direct `#endif` closes
14046        // this family. An error-owned terminator inside either branch belongs
14047        // to a damaged nested conditional, not to this one.
14048        return None;
14049    }
14050    // Pair concrete directive tokens while walking the recovered subtree. An
14051    // inner conditional may be represented only by its `#ifndef`/`#endif`
14052    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
14053    // sufficient: an inner `#endif` can otherwise be mistaken for this
14054    // conditional's displaced terminator.
14055    let mut displaced = None;
14056    let mut conditional_depth = 0usize;
14057    let mut stack = children_iter(conditional)
14058        .map(|child| (child, false))
14059        .collect::<Vec<_>>();
14060    stack.reverse();
14061    while let Some((node, inside_error)) = stack.pop() {
14062        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14063            continue;
14064        }
14065        if node != conditional
14066            && matches!(
14067                node.kind(),
14068                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
14069            )
14070        {
14071            continue;
14072        }
14073        let inside_error = inside_error || node.kind() == "ERROR";
14074        match node.kind() {
14075            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
14076                conditional_depth += 1;
14077            }
14078            "#endif" if !node.is_missing() => {
14079                if conditional_depth == 0
14080                    && inside_error
14081                    && displaced
14082                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
14083                {
14084                    displaced = Some(node);
14085                }
14086                conditional_depth = conditional_depth.saturating_sub(1);
14087            }
14088            _ => {}
14089        }
14090        let first_pushed = stack.len();
14091        stack.extend(children_iter(node).map(|child| (child, inside_error)));
14092        stack[first_pushed..].reverse();
14093    }
14094    displaced
14095}
14096
14097/// The first `#else`/`#elif` of `conditional` that tree-sitter displaced into
14098/// an `ERROR` inside the conditional's own branch.
14099///
14100/// The macro-declaration grammar can read an unterminated end sentinel, the
14101/// branch directive and the next branch's begin sentinel as one declaration
14102/// (`END_NS #else BEGIN_NS class C {};`, #6325): the `#else` becomes an
14103/// `ERROR` child and the conditional loses its `alternative`. The token is
14104/// still the structured branch boundary. Nested conditionals own their own
14105/// directives, including ones represented only by error-owned `#if`/`#endif`
14106/// tokens, so the walk pairs those the same way
14107/// [`cpp_displaced_preprocessor_terminator`] does.
14108pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14109    if !conditional.has_error() {
14110        return None;
14111    }
14112    let alternative = conditional.child_by_field_name("alternative");
14113    let mut conditional_depth = 0usize;
14114    let mut stack = children_iter(conditional)
14115        .filter(|child| Some(*child) != alternative)
14116        .map(|child| (child, false))
14117        .collect::<Vec<_>>();
14118    stack.reverse();
14119    while let Some((node, inside_error)) = stack.pop() {
14120        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14121            continue;
14122        }
14123        if matches!(
14124            node.kind(),
14125            "preproc_if"
14126                | "preproc_ifdef"
14127                | "preproc_ifndef"
14128                | "preproc_elif"
14129                | "preproc_elifdef"
14130                | "preproc_else"
14131        ) {
14132            continue;
14133        }
14134        let inside_error = inside_error || node.kind() == "ERROR";
14135        match node.kind() {
14136            "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14137            "#endif" if !node.is_missing() => {
14138                conditional_depth = conditional_depth.saturating_sub(1);
14139            }
14140            "#else" | "#elif" | "#elifdef" | "#elifndef"
14141                if inside_error && conditional_depth == 0 && !node.is_missing() =>
14142            {
14143                return Some(node);
14144            }
14145            _ => {}
14146        }
14147        let first_pushed = stack.len();
14148        stack.extend(children_iter(node).map(|child| (child, inside_error)));
14149        stack[first_pushed..].reverse();
14150    }
14151    None
14152}
14153
14154/// The effective end of a conditional whose real terminator tree-sitter
14155/// displaced into declaration recovery.
14156///
14157/// Most damaged conditionals retain a concrete `#endif` token below an
14158/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
14159/// boundary. A preprocessor family that selects the middle of a declaration
14160/// can lose the directive tokens entirely. In that shape tree-sitter leaves
14161/// the declaration's `typedef` token as the sole child of the immediately
14162/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
14163/// declarator name inside the conditional's first declaration. The declaration
14164/// end is then the smallest structured boundary that contains the whole split
14165/// declaration.
14166#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14167pub struct CppDisplacedPreprocessorBoundary {
14168    pub end_byte: usize,
14169    pub end_line: usize,
14170}
14171
14172pub fn cpp_displaced_preprocessor_boundary(
14173    conditional: Node<'_>,
14174) -> Option<CppDisplacedPreprocessorBoundary> {
14175    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14176        return Some(CppDisplacedPreprocessorBoundary {
14177            end_byte: terminator.end_byte(),
14178            end_line: terminator.end_position().row + 1,
14179        });
14180    }
14181    if let Some(declaration) = displaced_split_declaration(conditional) {
14182        return Some(CppDisplacedPreprocessorBoundary {
14183            end_byte: declaration.end_byte(),
14184            end_line: declaration.end_position().row + 1,
14185        });
14186    }
14187    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14188        return Some(CppDisplacedPreprocessorBoundary {
14189            end_byte: terminator.end_byte(),
14190            end_line: terminator.end_position().row + 1,
14191        });
14192    }
14193    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14194        return Some(CppDisplacedPreprocessorBoundary {
14195            end_byte: terminator.end_byte(),
14196            end_line: terminator.end_position().row + 1,
14197        });
14198    }
14199    None
14200}
14201
14202/// Recover an outer terminator that tree-sitter assigned to a damaged nested
14203/// conditional. This occurs when a split construct such as `extern "C"`
14204/// consumes the nested `#endif` inside an error node: the nested conditional's
14205/// direct terminator is then the outer conditional's real terminator, while
14206/// the outer node ends with a missing token and absorbs later declarations.
14207fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14208    if !conditional.has_error()
14209        || conditional.child_by_field_name("alternative").is_some()
14210        || conditional
14211            .child(conditional.child_count().saturating_sub(1))
14212            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14213    {
14214        return None;
14215    }
14216    let mut recovered = None;
14217    for nested in named_children_iter(conditional) {
14218        if !matches!(
14219            nested.kind(),
14220            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14221        ) || nested.child_by_field_name("alternative").is_some()
14222        {
14223            continue;
14224        }
14225        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14226            continue;
14227        };
14228        if direct.kind() != "#endif" || direct.is_missing() {
14229            continue;
14230        }
14231        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14232            continue;
14233        };
14234        if displaced.end_byte() >= direct.start_byte() {
14235            continue;
14236        }
14237        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14238            recovered = Some(direct);
14239        }
14240    }
14241    recovered
14242}
14243
14244fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14245    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14246        return None;
14247    }
14248    let mut cursor = conditional.walk();
14249    let declarations = conditional
14250        .named_children(&mut cursor)
14251        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14252        .collect::<Vec<_>>();
14253    let declaration = *declarations.first()?;
14254    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14255        return None;
14256    }
14257    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14258    let mut terminator = None;
14259    let mut stack = (0..declaration.child_count())
14260        .filter_map(|index| declaration.child(index))
14261        .filter(|child| child.start_byte() < declarator_start)
14262        .map(|child| (child, false))
14263        .collect::<Vec<_>>();
14264    while let Some((node, inside_error)) = stack.pop() {
14265        let inside_error = inside_error || node.kind() == "ERROR";
14266        if inside_error && node.kind() == "#endif" && !node.is_missing() {
14267            terminator = Some(node);
14268            continue;
14269        }
14270        for index in 0..node.child_count() {
14271            if let Some(child) = node.child(index)
14272                && child.start_byte() < declarator_start
14273            {
14274                stack.push((child, inside_error));
14275            }
14276        }
14277    }
14278    terminator
14279}
14280
14281fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14282    if !conditional.has_error()
14283        || conditional.child_by_field_name("alternative").is_some()
14284        || conditional
14285            .prev_named_sibling()
14286            .filter(|sibling| {
14287                sibling.kind() == "ERROR"
14288                    && sibling.child_count() == 1
14289                    && sibling
14290                        .child(0)
14291                        .is_some_and(|child| child.kind() == "typedef")
14292            })
14293            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14294            .is_none()
14295    {
14296        return None;
14297    }
14298    let mut cursor = conditional.walk();
14299    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14300    let declaration_index = children
14301        .iter()
14302        .position(|child| child.kind() == "declaration" && child.has_error())?;
14303    let declaration = children[declaration_index];
14304    if !children
14305        .iter()
14306        .skip(declaration_index + 1)
14307        .any(|child| child.end_byte() > declaration.end_byte())
14308    {
14309        return None;
14310    }
14311    let declarator = declaration.child_by_field_name("declarator")?;
14312    let mut error_end = None;
14313    let mut names = Vec::new();
14314    let mut stack = vec![declarator];
14315    while let Some(node) = stack.pop() {
14316        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14317            error_end =
14318                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14319            continue;
14320        }
14321        if matches!(node.kind(), "identifier" | "type_identifier") {
14322            names.push(node.start_byte());
14323        }
14324        push_named_children_reversed(node, &mut stack);
14325    }
14326    let error_end = error_end?;
14327    names
14328        .into_iter()
14329        .any(|start| start >= error_end)
14330        .then_some(declaration)
14331}
14332
14333fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14334    let Some(error) = parent.child(error_index) else {
14335        return false;
14336    };
14337    if error.kind() != "ERROR"
14338        || error.child_count() != 1
14339        || error.child(0).is_none_or(|child| child.kind() != "}")
14340    {
14341        return false;
14342    }
14343    let Some(semicolon) = parent.child(error_index + 1) else {
14344        return false;
14345    };
14346    semicolon.kind() == "expression_statement"
14347        && semicolon.child_count() == 1
14348        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14349}
14350
14351/// Locate the real end of a class-like declaration when a macro invocation
14352/// without a source semicolon absorbs the class's `};` into its parsed field.
14353/// The grammar then keeps following namespace declarations as later children
14354/// of the same field list. The direct ERROR-plus-semicolon pair proves the
14355/// boundary structurally; no source-text delimiter scan is needed.
14356fn displaced_macro_class_tail(
14357    declaration_node: Node<'_>,
14358    body: Node<'_>,
14359    source: &str,
14360) -> Option<DisplacedMacroClassTail> {
14361    if !matches!(
14362        declaration_node.kind(),
14363        "class_specifier" | "struct_specifier" | "union_specifier"
14364    ) || body.kind() != "field_declaration_list"
14365    {
14366        return None;
14367    }
14368
14369    let child_count = body.named_child_count();
14370    for index in 0..child_count {
14371        let child = body.named_child(index)?;
14372        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14373            continue;
14374        };
14375        let split_index = index + 1;
14376        if split_index >= child_count {
14377            return None;
14378        }
14379        let mut cursor = body.walk();
14380        if !body
14381            .named_children(&mut cursor)
14382            .skip(split_index)
14383            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14384        {
14385            return None;
14386        }
14387        return Some(DisplacedMacroClassTail {
14388            split_index,
14389            class_range: Range {
14390                start_byte: declaration_node.start_byte(),
14391                end_byte: terminator.end_byte(),
14392                start_line: declaration_node.start_position().row + 1,
14393                end_line: terminator.end_position().row + 1,
14394            },
14395        });
14396    }
14397    None
14398}
14399
14400fn displaced_macro_field_terminator<'tree>(
14401    field: Node<'tree>,
14402    source: &str,
14403) -> Option<Node<'tree>> {
14404    if field.kind() != "field_declaration" {
14405        return None;
14406    }
14407    // The older grammar reads the unterminated `MACRO(arg)` invocation as a
14408    // macro-token type and a `parenthesized_declarator`; the macro-declaration
14409    // grammar reads the whole invocation as a `macro_type_specifier`.
14410    let macro_type = field.child_by_field_name("type")?;
14411    let macro_name = match macro_type.kind() {
14412        "type_identifier"
14413            if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14414        {
14415            macro_type
14416        }
14417        "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14418        _ => return None,
14419    };
14420    if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14421        return None;
14422    }
14423    for index in 0..field.child_count() {
14424        let error = field.child(index)?;
14425        if error.kind() != "ERROR"
14426            || error.child_count() != 1
14427            || error.child(0).is_none_or(|child| child.kind() != "}")
14428        {
14429            continue;
14430        }
14431        let semicolon = field.child(index + 1)?;
14432        if semicolon.kind() == ";" {
14433            return Some(semicolon);
14434        }
14435    }
14436    None
14437}
14438
14439fn recover_fragmented_partial_specialization<'tree>(
14440    template_node: Node<'tree>,
14441    declaration_child: Node<'tree>,
14442    source: &str,
14443    ancestry: &ParentIndex<'tree>,
14444) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14445    if declaration_child.kind() != "function_definition" {
14446        return None;
14447    }
14448    let class_node = declaration_child.child_by_field_name("type")?;
14449    if !matches!(
14450        class_node.kind(),
14451        "class_specifier" | "struct_specifier" | "union_specifier"
14452    ) || !class_node
14453        .child_by_field_name("name")
14454        .and_then(|name| direct_identifier_name(name, source))
14455        .is_some_and(|name| cpp_export_macro_token(&name))
14456    {
14457        return None;
14458    }
14459    let declarator = declaration_child.child_by_field_name("declarator")?;
14460    if declarator.kind() != "template_function" {
14461        return None;
14462    }
14463    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14464    if metadata.specialization_arguments.is_empty() {
14465        return None;
14466    }
14467    let body = declaration_child.child_by_field_name("body")?;
14468    if body.kind() != "compound_statement" {
14469        return None;
14470    }
14471    let complete_prefix = body.named_child(0).filter(|first| {
14472        first.kind() == "labeled_statement"
14473            && first.has_error()
14474            && first
14475                .named_child(first.named_child_count().saturating_sub(1))
14476                .is_some_and(recovered_declaration_has_class_terminator)
14477    });
14478    let complete_body = complete_prefix.is_some();
14479    let mut prefix_members = Vec::new();
14480    if let Some(prefix) = complete_prefix {
14481        prefix_members.push(prefix);
14482    } else {
14483        let mut body_cursor = body.walk();
14484        for child in body.named_children(&mut body_cursor) {
14485            if !is_structurally_valid_fragmented_class_prefix_member(child) {
14486                break;
14487            }
14488            prefix_members.push(child);
14489        }
14490    }
14491    let containing_declarations = template_node.parent()?;
14492    if !matches!(
14493        containing_declarations.kind(),
14494        "declaration_list" | "compound_statement"
14495    ) {
14496        return None;
14497    }
14498    let mut member_siblings = Vec::new();
14499    let mut following_declarations = Vec::new();
14500    let terminator;
14501    if complete_body {
14502        terminator = complete_prefix?;
14503        let mut cursor = body.walk();
14504        let mut after_prefix = false;
14505        for child in body.named_children(&mut cursor) {
14506            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14507                after_prefix = true;
14508            } else if after_prefix {
14509                following_declarations.push(child);
14510            }
14511        }
14512    } else {
14513        let mut found_template = false;
14514        let mut cursor = containing_declarations.walk();
14515        let mut class_terminator = None;
14516        for child in containing_declarations.children(&mut cursor) {
14517            if same_node(child, template_node) {
14518                found_template = true;
14519                continue;
14520            }
14521            if found_template && child.kind() == "}" {
14522                class_terminator = Some(child);
14523                break;
14524            }
14525            // A namespace can never be a class member: reaching one before the
14526            // terminator proves the class's true close was swallowed upstream
14527            // and this scan has crossed into the enclosing scope, so the
14528            // recovery cannot be bounded -- continuing re-owns the namespace
14529            // block (and its template specializations) as class members under
14530            // a re-appended package, desyncing the fq boundary (#2306).
14531            if found_template && child.kind() == "namespace_definition" {
14532                return None;
14533            }
14534            if found_template && child.is_named() {
14535                member_siblings.push(child);
14536            }
14537        }
14538        terminator = class_terminator?;
14539    }
14540    let name = format!(
14541        "{}<{}>",
14542        metadata.primary_name,
14543        metadata
14544            .specialization_arguments
14545            .iter()
14546            .map(|argument| argument.text.as_str())
14547            .collect::<Vec<_>>()
14548            .join(", ")
14549    );
14550    Some(RecoveredFragmentedPartialSpecialization {
14551        declaration_node: declaration_child,
14552        name,
14553        range: Range {
14554            start_byte: declaration_child.start_byte(),
14555            end_byte: terminator.end_byte(),
14556            start_line: declaration_child.start_position().row + 1,
14557            end_line: terminator.end_position().row + 1,
14558        },
14559        prefix_members,
14560        member_siblings,
14561        following_declarations,
14562    })
14563}
14564
14565/// Whether `node` is the function-shaped parser wrapper whose body was
14566/// recovered as a fragmented partial-specialization class body.
14567///
14568/// Consumers that walk lexical function scopes must use the same complete
14569/// structural proof as declaration extraction. Otherwise member callables in
14570/// the recovered body are mistaken for function-local declarations.
14571pub fn is_recovered_fragmented_partial_specialization_container(
14572    node: Node<'_>,
14573    source: &str,
14574) -> bool {
14575    let Some(template) = node
14576        .parent()
14577        .filter(|parent| parent.kind() == "template_declaration")
14578    else {
14579        return false;
14580    };
14581    let mut root = template;
14582    while let Some(parent) = root.parent() {
14583        root = parent;
14584    }
14585    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14586        .is_some()
14587}
14588
14589fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14590    if declaration.kind() != "declaration" {
14591        return false;
14592    }
14593    // With an export macro between `class` and its name, tree-sitter folds a
14594    // complete class body into a function-shaped declaration. The class's own
14595    // `};` remains structurally identifiable as a direct ERROR child holding
14596    // `}`, immediately followed by the declaration's direct `;` child.
14597    (0..declaration.child_count().saturating_sub(1)).any(|index| {
14598        let Some(error) = declaration.child(index) else {
14599            return false;
14600        };
14601        error.kind() == "ERROR"
14602            && error.child_count() == 1
14603            && error.child(0).is_some_and(|child| child.kind() == "}")
14604            && declaration
14605                .child(index + 1)
14606                .is_some_and(|child| child.kind() == ";")
14607    })
14608}
14609
14610fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14611    if node.has_error() {
14612        return false;
14613    }
14614    match node.kind() {
14615        "declaration"
14616        | "field_declaration"
14617        | "alias_declaration"
14618        | "type_definition"
14619        | "static_assert_declaration" => true,
14620        "labeled_statement" => node
14621            .named_child(node.named_child_count().saturating_sub(1))
14622            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14623        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14624            matches!(
14625                child.kind(),
14626                "declaration"
14627                    | "field_declaration"
14628                    | "alias_declaration"
14629                    | "type_definition"
14630                    | "function_definition"
14631            )
14632        }),
14633        _ => false,
14634    }
14635}
14636
14637fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14638    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14639        .then(|| node.child_by_field_name("declarator"))
14640        .flatten()
14641        .and_then(|declarator| extract_variable_name(declarator, source))
14642}
14643
14644use crate::graph::syntax::has_function_scope_ancestor;
14645
14646fn cpp_template_metadata<'tree>(
14647    template_node: Node<'tree>,
14648    declaration_child: Node<'tree>,
14649    source: &str,
14650    ancestry: &ParentIndex<'tree>,
14651) -> Option<CppTemplateMetadata> {
14652    let parameters_node = template_node.child_by_field_name("parameters")?;
14653    let name_node = cpp_templated_class_name_node(declaration_child)?;
14654    let primary_node = match name_node.kind() {
14655        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14656        _ => name_node,
14657    };
14658    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14659    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14660        return None;
14661    }
14662
14663    let mut parameter_nodes = Vec::new();
14664    let mut parameter_names = Vec::new();
14665    let mut cursor = parameters_node.walk();
14666    for parameter in parameters_node.named_children(&mut cursor) {
14667        if !matches!(
14668            parameter.kind(),
14669            "type_parameter_declaration"
14670                | "optional_type_parameter_declaration"
14671                | "variadic_type_parameter_declaration"
14672                | "template_template_parameter_declaration"
14673                | "parameter_declaration"
14674                | "optional_parameter_declaration"
14675                | "variadic_parameter_declaration"
14676        ) {
14677            continue;
14678        }
14679        let index = parameter_nodes.len();
14680        // An unnamed parameter still contributes template arity and kind. Use
14681        // an impossible C++ identifier so positional reconciliation can bind
14682        // it without making source expressions refer to a name that was not
14683        // written.
14684        let name = cpp_template_parameter_name(parameter, source)
14685            .unwrap_or_else(|| format!("<anonymous:{index}>"));
14686        parameter_names.push(name);
14687        parameter_nodes.push(parameter);
14688    }
14689    let parameters = parameter_nodes
14690        .into_iter()
14691        .zip(parameter_names.iter().cloned())
14692        .map(|(parameter, name)| CppTemplateParameterMetadata {
14693            name,
14694            kind: cpp_template_parameter_kind(parameter),
14695            variadic: matches!(
14696                parameter.kind(),
14697                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14698            ),
14699            default: cpp_template_parameter_default_expression(
14700                parameter,
14701                source,
14702                &parameter_names,
14703                ancestry,
14704            ),
14705        })
14706        .collect();
14707    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14708        Vec::new()
14709    } else {
14710        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
14711            .unwrap_or_default()
14712    };
14713    let alias_target = (declaration_child.kind() == "alias_declaration")
14714        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
14715        .flatten();
14716    Some(CppTemplateMetadata {
14717        primary_name,
14718        primary_fq_name: String::new(),
14719        parameters,
14720        specialization_arguments,
14721        alias_target,
14722    })
14723}
14724
14725fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14726    match node.kind() {
14727        "class_specifier" | "struct_specifier" | "union_specifier" => {
14728            node.child_by_field_name("name")
14729        }
14730        "function_definition" => {
14731            let declarator = node.child_by_field_name("declarator")?;
14732            if matches!(declarator.kind(), "identifier" | "template_function") {
14733                Some(declarator)
14734            } else {
14735                None
14736            }
14737        }
14738        "alias_declaration" => node.child_by_field_name("name"),
14739        _ => None,
14740    }
14741}
14742
14743fn cpp_template_alias_target<'tree>(
14744    alias: Node<'tree>,
14745    source: &str,
14746    parameter_names: &[String],
14747    ancestry: &ParentIndex<'tree>,
14748) -> Option<CppTemplateAliasTargetMetadata> {
14749    let mut type_node = alias.child_by_field_name("type")?;
14750    while type_node.kind() == "type_descriptor" {
14751        type_node = type_node.child_by_field_name("type")?;
14752    }
14753    let global = type_node.child_by_field_name("scope").is_none()
14754        && type_node.child(0).is_some_and(|child| child.kind() == "::");
14755    let mut components = Vec::new();
14756    cpp_template_target_components(type_node, source, &mut components)?;
14757    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14758    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14759        components,
14760        global,
14761        arguments,
14762    })
14763}
14764
14765fn cpp_template_target_components(
14766    node: Node<'_>,
14767    source: &str,
14768    out: &mut Vec<String>,
14769) -> Option<()> {
14770    // Delay reading the terminal name until the scope has been processed,
14771    // preserving the partial output even when a later component is missing.
14772    enum Component<'tree> {
14773        Node(Node<'tree>),
14774        Name(Node<'tree>),
14775    }
14776    let mut stack = vec![Component::Node(node)];
14777    while let Some(component) = stack.pop() {
14778        let node = match component {
14779            Component::Node(node) => node,
14780            Component::Name(node) => node.child_by_field_name("name")?,
14781        };
14782        match node.kind() {
14783            "identifier" | "namespace_identifier" | "type_identifier" => {
14784                out.push(node_text(node, source).to_string());
14785            }
14786            "template_type" => stack.push(Component::Name(node)),
14787            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14788                stack.push(Component::Name(node));
14789                if let Some(scope) = node.child_by_field_name("scope") {
14790                    stack.push(Component::Node(scope));
14791                }
14792            }
14793            _ => return None,
14794        }
14795    }
14796    Some(())
14797}
14798
14799fn cpp_template_argument_expressions<'tree>(
14800    mut node: Node<'tree>,
14801    source: &str,
14802    parameter_names: &[String],
14803    ancestry: &ParentIndex<'tree>,
14804) -> Option<Vec<CppTemplateExpression>> {
14805    loop {
14806        match node.kind() {
14807            "template_type" | "template_function" => {
14808                let arguments = node.child_by_field_name("arguments")?;
14809                let mut cursor = arguments.walk();
14810                return Some(
14811                    arguments
14812                        .named_children(&mut cursor)
14813                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14814                        .map(|argument| {
14815                            cpp_template_expression(argument, source, parameter_names, ancestry)
14816                        })
14817                        .collect(),
14818                );
14819            }
14820            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14821                node = node
14822                    .child_by_field_name("name")
14823                    .or_else(|| node.child_by_field_name("type"))?;
14824            }
14825            _ => return None,
14826        }
14827    }
14828}
14829
14830fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14831    if node.kind() == "template_template_parameter_declaration" {
14832        let mut cursor = node.walk();
14833        return node
14834            .named_children(&mut cursor)
14835            .find(|child| {
14836                matches!(
14837                    child.kind(),
14838                    "type_parameter_declaration"
14839                        | "optional_type_parameter_declaration"
14840                        | "variadic_type_parameter_declaration"
14841                )
14842            })
14843            .expect("a template-template parameter has a type parameter declaration");
14844    }
14845    node
14846}
14847
14848pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14849    let node = cpp_template_parameter_declaration(node);
14850    let candidate = node
14851        .child_by_field_name("name")
14852        .or_else(|| node.child_by_field_name("declarator"))
14853        .or_else(|| cpp_recovered_type_parameter_name(node))
14854        .or_else(|| {
14855            let mut cursor = node.walk();
14856            node.named_children(&mut cursor).find(|child| {
14857                matches!(
14858                    child.kind(),
14859                    "identifier" | "type_identifier" | "field_identifier"
14860                )
14861            })
14862        })?;
14863    if let Some(name) = direct_identifier_name(candidate, source) {
14864        return Some(name);
14865    }
14866    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14867    (!name.is_empty()).then_some(name)
14868}
14869
14870pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14871    match node.kind() {
14872        "type_parameter_declaration"
14873        | "optional_type_parameter_declaration"
14874        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14875        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14876        _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14877        _ => CppTemplateParameterKind::Value,
14878    }
14879}
14880
14881/// The name of a defaulted type parameter that tree-sitter-cpp recovered as a
14882/// non-type parameter. The grammar's `default_type` cannot hold an abstract
14883/// declarator, so `typename U = T*` always recovers the `*` as an ERROR
14884/// sibling; since grammar 0.24.4 the parameter itself also becomes an
14885/// `optional_parameter_declaration` with `type: dependent_type(typename U)`,
14886/// no declarator and `default_value: T`. A valid dependent type names a
14887/// qualified type after `typename`, so a bare type identifier there is the
14888/// type parameter's name.
14889pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14890    if parameter.kind() != "optional_parameter_declaration"
14891        || parameter.child_by_field_name("declarator").is_some()
14892    {
14893        return None;
14894    }
14895    let dependent = parameter
14896        .child_by_field_name("type")
14897        .filter(|type_node| type_node.kind() == "dependent_type")?;
14898    let mut cursor = dependent.walk();
14899    let mut named = dependent
14900        .named_children(&mut cursor)
14901        .filter(|child| !child.is_extra());
14902    match (named.next(), named.next()) {
14903        (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14904        _ => None,
14905    }
14906}
14907
14908#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14909pub struct CppTemplateParameterNameMatches {
14910    pub any_parameter: bool,
14911    pub type_parameter: bool,
14912}
14913
14914pub fn cpp_template_parameter_name_matches(
14915    parameters: Node<'_>,
14916    name: &str,
14917    mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14918) -> CppTemplateParameterNameMatches {
14919    let mut matches = CppTemplateParameterNameMatches::default();
14920    let mut cursor = parameters.walk();
14921    for parameter in parameters.named_children(&mut cursor) {
14922        if parameter_name(parameter).as_deref() == Some(name) {
14923            matches.any_parameter = true;
14924            matches.type_parameter |=
14925                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14926        }
14927    }
14928    matches
14929}
14930
14931pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14932    parent.child_by_field_name("type").is_some_and(|type_node| {
14933        type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14934    })
14935}
14936
14937fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14938    let node = cpp_template_parameter_declaration(node);
14939    node.child_by_field_name("default_type")
14940        .or_else(|| node.child_by_field_name("default_value"))
14941}
14942
14943fn cpp_template_parameter_default_expression<'tree>(
14944    parameter: Node<'tree>,
14945    source: &str,
14946    parameter_names: &[String],
14947    ancestry: &ParentIndex<'tree>,
14948) -> Option<CppTemplateExpression> {
14949    let default = cpp_template_parameter_default(parameter)?;
14950    let base = cpp_template_expression(default, source, parameter_names, ancestry);
14951    let Some(pointer_error) = parameter.next_named_sibling() else {
14952        return Some(base);
14953    };
14954    // The grammar's default_type accepts a type specifier, not a complete
14955    // type-id. For `const T*` it calls `const` the type and leaves `T*` in
14956    // the next ERROR. Let the type-id grammar interpret that exact range.
14957    if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14958        && pointer_error.is_error()
14959    {
14960        let text = &source[default.start_byte()..pointer_error.end_byte()];
14961        let parsed = crate::graph::syntax::parse_sentinel(
14962            "using __bifrost_default = ",
14963            crate::graph::syntax::SentinelInput::region(text),
14964            ";",
14965        );
14966        let root = parsed.tree.root_node();
14967        if !root.has_error()
14968            && let Some(type_node) =
14969                brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
14970                    root,
14971                    |child| !child.is_extra(),
14972                )
14973                .filter(|alias| alias.kind() == "alias_declaration")
14974                .and_then(|alias| alias.child_by_field_name("type"))
14975        {
14976            let alias_ancestry = ParentIndex::new(root);
14977            return Some(cpp_template_expression(
14978                type_node,
14979                &parsed.source,
14980                parameter_names,
14981                &alias_ancestry,
14982            ));
14983        }
14984    }
14985    let Some(pointer_declarator) =
14986        recovered_abstract_pointer_declarator_term(pointer_error, source)
14987    else {
14988        return Some(base);
14989    };
14990    Some(CppTemplateExpression {
14991        text: format!(
14992            "{}{}",
14993            base.text,
14994            normalize_cpp_whitespace(node_text(pointer_error, source))
14995        ),
14996        term: CppTemplateTerm::Node {
14997            kind: "type_descriptor".to_string(),
14998            children: vec![base.term, pointer_declarator],
14999        },
15000    })
15001}
15002
15003fn recovered_abstract_pointer_declarator_term(
15004    node: Node<'_>,
15005    source: &str,
15006) -> Option<CppTemplateTerm> {
15007    if node.kind() != "ERROR" || node.child_count() == 0 {
15008        return None;
15009    }
15010    let mut children = Vec::new();
15011    for index in 0..node.child_count() {
15012        let child = node.child(index)?;
15013        if child.kind() != "*" {
15014            return None;
15015        }
15016        children.push(CppTemplateTerm::Atom {
15017            kind: "*".to_string(),
15018            text: normalize_cpp_whitespace(node_text(child, source)),
15019        });
15020    }
15021    Some(CppTemplateTerm::Node {
15022        kind: "abstract_pointer_declarator".to_string(),
15023        children,
15024    })
15025}
15026
15027fn cpp_template_expression<'tree>(
15028    node: Node<'tree>,
15029    source: &str,
15030    parameter_names: &[String],
15031    ancestry: &ParentIndex<'tree>,
15032) -> CppTemplateExpression {
15033    let text = normalize_cpp_whitespace(node_text(node, source));
15034    CppTemplateExpression {
15035        text,
15036        term: cpp_template_term(node, source, parameter_names, ancestry),
15037    }
15038}
15039
15040pub fn cpp_template_term<'tree>(
15041    node: Node<'tree>,
15042    source: &str,
15043    parameter_names: &[String],
15044    ancestry: &ParentIndex<'tree>,
15045) -> CppTemplateTerm {
15046    enum Work<'tree> {
15047        Visit(Node<'tree>),
15048        Build { kind: String, child_count: usize },
15049    }
15050
15051    let mut work = vec![Work::Visit(node)];
15052    let mut terms = Vec::new();
15053    while let Some(next) = work.pop() {
15054        match next {
15055            Work::Visit(current) => {
15056                let text = normalize_cpp_whitespace(node_text(current, source));
15057                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
15058                    terms.push(CppTemplateTerm::Parameter(text));
15059                    continue;
15060                }
15061                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
15062                    let mut cursor = current.walk();
15063                    let named = current
15064                        .named_children(&mut cursor)
15065                        .filter(|child| !child.is_extra() && child.kind() != "comment")
15066                        .collect::<Vec<_>>();
15067                    if let [child] = named.as_slice() {
15068                        work.push(Work::Visit(*child));
15069                        continue;
15070                    }
15071                }
15072                if current.child_count() == 0 {
15073                    terms.push(CppTemplateTerm::Atom {
15074                        kind: if matches!(
15075                            current.kind(),
15076                            "identifier"
15077                                | "type_identifier"
15078                                | "field_identifier"
15079                                | "namespace_identifier"
15080                        ) {
15081                            "identifier".to_string()
15082                        } else {
15083                            current.kind().to_string()
15084                        },
15085                        text,
15086                    });
15087                    continue;
15088                }
15089                let children = (0..current.child_count())
15090                    .filter_map(|index| current.child(index))
15091                    .filter(|child| !child.is_extra() && child.kind() != "comment")
15092                    .collect::<Vec<_>>();
15093                work.push(Work::Build {
15094                    kind: current.kind().to_string(),
15095                    child_count: children.len(),
15096                });
15097                work.extend(children.into_iter().rev().map(Work::Visit));
15098            }
15099            Work::Build { kind, child_count } => {
15100                let children = terms.split_off(terms.len() - child_count);
15101                terms.push(CppTemplateTerm::Node { kind, children });
15102            }
15103        }
15104    }
15105    terms.pop().expect("template term traversal emits one root")
15106}
15107
15108fn cpp_template_term_leaf_is_parameter<'tree>(
15109    node: Node<'tree>,
15110    text: &str,
15111    parameter_names: &[String],
15112    ancestry: &ParentIndex<'tree>,
15113) -> bool {
15114    if !parameter_names.iter().any(|parameter| parameter == text) {
15115        return false;
15116    }
15117    !ancestry.parent(node).is_some_and(|parent| {
15118        matches!(
15119            parent.kind(),
15120            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
15121        ) && parent.child_by_field_name("scope").is_some()
15122            && parent.child_by_field_name("name") == Some(node)
15123    })
15124}
15125
15126fn enclosing_cpp_declaration_node<'tree>(
15127    mut node: Node<'tree>,
15128    ancestry: &ParentIndex<'tree>,
15129) -> Option<Node<'tree>> {
15130    loop {
15131        match node.kind() {
15132            "declaration"
15133            | "function_declaration"
15134            | "field_declaration"
15135            | "function_definition" => return Some(node),
15136            _ => node = ancestry.parent(node)?,
15137        }
15138    }
15139}
15140
15141fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15142    let mut params = Vec::new();
15143    let mut cursor = parameters_node.walk();
15144    for child in parameters_node.children(&mut cursor) {
15145        match child.kind() {
15146            "explicit_object_parameter_declaration" => {
15147                if let Some(parameter) = child
15148                    .named_children(&mut child.walk())
15149                    .find(|node| node.kind() == "parameter_declaration")
15150                {
15151                    params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15152                }
15153            }
15154            "parameter_declaration" | "optional_parameter_declaration" => {
15155                params.push(cpp_parameter_type(child, source));
15156            }
15157            "variadic_parameter_declaration" => {
15158                params.push(cpp_parameter_type(child, source));
15159            }
15160            "variadic_parameter" | "..." => params.push("...".to_string()),
15161            _ => {}
15162        }
15163    }
15164
15165    if params.is_empty() {
15166        "()".to_string()
15167    } else {
15168        format!("({})", params.join(", "))
15169    }
15170}
15171
15172/// Where a C or C++ callable declaration is written, which is what decides
15173/// which modifier facts its own node is able to state.
15174enum CppCallableScope<'tree> {
15175    /// Written directly in a class, struct or union body. The declaration
15176    /// states its own storage class, and its access is the one the body's
15177    /// specifier ladder is under at that point.
15178    ClassBody {
15179        body: Node<'tree>,
15180        default_visibility: DeclaredVisibility,
15181    },
15182    /// A qualified declaration written outside every class body
15183    /// (`void Client::send(int) {}`). C++ forbids repeating `static` here and
15184    /// writes no access specifier, and whether the qualifier names a class or
15185    /// a namespace is not decidable from this file alone -- that is exactly
15186    /// what [`crate::reconcile`] needs the include-visible class table for.
15187    Qualified,
15188    /// Namespace scope or file scope, reached by an unqualified name. C has no
15189    /// member functions at all, so every C callable lands here.
15190    Unqualified,
15191}
15192
15193/// Where the declaration sits, from one upward walk of its own ancestry.
15194///
15195/// The walk stops as soon as something proves the declaration is outside every
15196/// class body: a `namespace_definition`, because a class body never encloses
15197/// one, and a `compound_statement`, because a body opens a block scope and
15198/// nothing declared in it is a member of the class the enclosing definition
15199/// belongs to. That is the same rule
15200/// `CppVisitor::visit_local_types_in_function` applies when it
15201/// drops the aggregate carrier, and it keeps this question off the O(depth)
15202/// walk a deeply nested generated header would otherwise pay per callable.
15203///
15204/// `function_declarator` is `None` for a declaration whose callable the macro
15205/// recovery reparsed out of a malformed member: there is no declarator node to
15206/// ask whether the name carries a qualifier, and a recovered member is written
15207/// in its class body anyway.
15208fn cpp_callable_scope<'tree>(
15209    declaration: Node<'tree>,
15210    function_declarator: Option<Node<'tree>>,
15211    ancestry: &ParentIndex<'tree>,
15212) -> CppCallableScope<'tree> {
15213    let mut current = Some(declaration);
15214    while let Some(node) = current {
15215        match node.kind() {
15216            "field_declaration_list" => {
15217                let default_visibility =
15218                    ancestry
15219                        .parent(node)
15220                        .map_or(DeclaredVisibility::Unknown, |class_like| {
15221                            // `struct` and `union` members default to public;
15222                            // `class` members default to private.
15223                            match class_like.kind() {
15224                                "struct_specifier" | "union_specifier" => {
15225                                    DeclaredVisibility::Public
15226                                }
15227                                _ => DeclaredVisibility::Private,
15228                            }
15229                        });
15230                return CppCallableScope::ClassBody {
15231                    body: node,
15232                    default_visibility,
15233                };
15234            }
15235            "namespace_definition" | "compound_statement" | "translation_unit" => break,
15236            _ => {}
15237        }
15238        current = ancestry.parent(node);
15239    }
15240    if cpp_callable_declarator_is_qualified(function_declarator) {
15241        CppCallableScope::Qualified
15242    } else {
15243        CppCallableScope::Unqualified
15244    }
15245}
15246
15247/// Whether the declarator's own written name carries a `::` qualifier.
15248///
15249/// A qualified declaration written outside every class body
15250/// (`long Client::send(int) {}`) names a member whose class body this file may
15251/// not contain, which is what [`cpp_callable_scope`] calls `Qualified` and the
15252/// reason [`cpp_callable_dispatch_extensibility`] refuses to answer for one.
15253fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15254    function_declarator
15255        .and_then(cpp_function_declarator_name_node)
15256        .is_some_and(|name| name.kind() == "qualified_identifier")
15257}
15258
15259/// The access one class-body member declaration is written under.
15260///
15261/// C++ states member access positionally: an `access_specifier` applies to
15262/// every member written after it until the next one, and the members before
15263/// the first one take the class keyword's default. The answer is therefore the
15264/// last specifier the body writes before the member's own list-level node,
15265/// read from the specifier's own keyword token rather than from its text. A
15266/// label a macro invocation swallowed has no specifier node or keyword token;
15267/// [`cpp_macro_swallowed_access_label`] reads it from the declarator the
15268/// grammar made of the keyword.
15269///
15270/// The body's children are read forward with one cursor rather than backwards
15271/// from the member: `Node::prev_sibling` re-descends from the root for each
15272/// step, which is the per-edge cost #2361 keeps off this walk.
15273fn cpp_member_declared_visibility<'tree>(
15274    declaration: Node<'tree>,
15275    body: Node<'tree>,
15276    default_visibility: DeclaredVisibility,
15277    source: &str,
15278    ancestry: &ParentIndex<'tree>,
15279) -> DeclaredVisibility {
15280    let mut list_level = declaration;
15281    while ancestry.parent(list_level) != Some(body) {
15282        let Some(parent) = ancestry.parent(list_level) else {
15283            return default_visibility;
15284        };
15285        list_level = parent;
15286    }
15287    let mut visibility = default_visibility;
15288    let mut cursor = body.walk();
15289    for child in body.children(&mut cursor) {
15290        if child == list_level {
15291            break;
15292        }
15293        let access = if child.kind() == "access_specifier" {
15294            child.child(0).map(|keyword| keyword.kind())
15295        } else {
15296            cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15297        };
15298        visibility = match access {
15299            Some("public") => DeclaredVisibility::Public,
15300            Some("protected") => DeclaredVisibility::Protected,
15301            Some("private") => DeclaredVisibility::Private,
15302            _ => visibility,
15303        };
15304    }
15305    visibility
15306}
15307
15308/// Whether this declaration writes the `static` storage class specifier.
15309///
15310/// What `static` *means* is the scope's business: on a class-body member it is
15311/// the receiver fact, and at file scope it is internal linkage, which is why
15312/// both [`cpp_callable_modifiers`] and [`cpp_callable_linkage`] ask the same
15313/// question and answer it differently.
15314fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15315    let mut cursor = declaration.walk();
15316    declaration.named_children(&mut cursor).any(|child| {
15317        child.kind() == "storage_class_specifier"
15318            && normalize_cpp_whitespace(node_text(child, source)) == "static"
15319    })
15320}
15321
15322/// The three modifier facts [`SignatureMetadata::with_callable_modifiers`]
15323/// publishes, as C and C++ state them.
15324struct CppCallableModifiers {
15325    is_static: bool,
15326    is_constructor: bool,
15327    visibility: DeclaredVisibility,
15328}
15329
15330/// The modifier facts a C or C++ callable declaration states in its own shape.
15331///
15332/// C++ has no `static` keyword for receivers and no visibility keyword of the
15333/// Java kind: it states both in the declaration's position. A member declared
15334/// in a class body binds the object it is selected on unless that declaration
15335/// writes the `static` storage class, and its access is the one the body's
15336/// specifier ladder is under. A function at namespace or file scope binds
15337/// nothing and has no access specifier at all, so its declared visibility is
15338/// Unknown. Linkage is recorded separately and does not declare access. C has
15339/// no member functions, and the `static` a C function writes is internal linkage,
15340/// never a receiver fact.
15341///
15342/// Recording these is what lets `receiver_contract_of` answer for a C or C++
15343/// declaration. Without them it reported no contract at all,
15344/// `modeled_procedure_key_for_unit` refused to key any C or C++ workspace
15345/// callable, no reviewed summary could name one, and every effect or taint walk
15346/// that reached one reported `callee_unkeyable` (#3493). Go closed the same gap
15347/// in #3455, Kotlin in #3453, Python in #3451.
15348///
15349/// An out-of-line member definition cannot repeat `static` or access. These
15350/// are per-file facts only: the analyzer reconciles the exact overload with
15351/// include-visible class declarations before exposing its receiver contract
15352/// (#3509). A missing declaration leaves that contract unknown.
15353fn cpp_callable_modifiers<'tree>(
15354    declaration: Node<'tree>,
15355    function_declarator: Option<Node<'tree>>,
15356    is_constructor: bool,
15357    source: &str,
15358    ancestry: &ParentIndex<'tree>,
15359) -> CppCallableModifiers {
15360    match cpp_callable_scope(declaration, function_declarator, ancestry) {
15361        CppCallableScope::ClassBody {
15362            body,
15363            default_visibility,
15364        } => CppCallableModifiers {
15365            is_static: cpp_declares_static_storage_class(declaration, source),
15366            is_constructor,
15367            visibility: cpp_member_declared_visibility(
15368                declaration,
15369                body,
15370                default_visibility,
15371                source,
15372                ancestry,
15373            ),
15374        },
15375        CppCallableScope::Qualified => CppCallableModifiers {
15376            is_static: false,
15377            is_constructor,
15378            visibility: DeclaredVisibility::Unknown,
15379        },
15380        CppCallableScope::Unqualified => CppCallableModifiers {
15381            is_static: false,
15382            is_constructor,
15383            visibility: DeclaredVisibility::Unknown,
15384        },
15385    }
15386}
15387
15388/// The persisted signature facts one C or C++ callable declaration publishes.
15389///
15390/// `linkage` is the caller's, not this function's: the node that carries the
15391/// storage class is not always the declaration this declarator sits in -- a
15392/// recovered constructor reads it from its class declaration, and a
15393/// macro-wrapped declaration reads the `static` its recovery recorded -- so the
15394/// visit site that knows which node states it passes the answer in. Recording
15395/// it here keeps it beside the declared visibility it also decides.
15396fn cpp_signature_metadata<'tree>(
15397    signature: String,
15398    function_declarator: Node<'tree>,
15399    linkage: CallableLinkage,
15400    source: &str,
15401    ancestry: &ParentIndex<'tree>,
15402) -> SignatureMetadata {
15403    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15404    let is_constructor =
15405        cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15406    let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15407        Some(declaration) => cpp_callable_modifiers(
15408            declaration,
15409            Some(function_declarator),
15410            is_constructor,
15411            source,
15412            ancestry,
15413        ),
15414        // Severe recovery left no declaration node above this declarator, so
15415        // nothing is stated here beyond what the callable's own name proves.
15416        None => CppCallableModifiers {
15417            is_static: false,
15418            is_constructor,
15419            visibility: DeclaredVisibility::Unknown,
15420        },
15421    };
15422    let enrich = |mut metadata: SignatureMetadata| {
15423        metadata = metadata.with_overload_discriminator(Some(cpp_overload_discriminator(
15424            function_declarator,
15425            source,
15426        )));
15427        // #3507: a qualified definition repeats none of the class body's
15428        // dispatch facts, so this row publishes none either.
15429        if let Some(dispatch) = dispatch {
15430            metadata = metadata.with_dispatch_extensibility(dispatch);
15431        }
15432        metadata
15433            .with_callable_linkage(linkage)
15434            .with_callable_modifiers(
15435                modifiers.is_static,
15436                modifiers.is_constructor,
15437                modifiers.visibility,
15438            )
15439    };
15440    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15441    let return_type_identity =
15442        cpp_callable_return_type_identity(function_declarator, source, ancestry);
15443    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15444        return enrich(
15445            SignatureMetadata::new(signature, Vec::new())
15446                .with_return_type_text(return_type_text)
15447                .with_return_type_identity(return_type_identity),
15448        );
15449    };
15450    let callable_arity = cpp_callable_arity(parameters_node, source);
15451    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15452    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15453    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15454    let Some(relative_start) = signature
15455        .get(search_from..)
15456        .and_then(|suffix| suffix.find(&parameter_text))
15457    else {
15458        return enrich(
15459            SignatureMetadata::new(signature, Vec::new())
15460                .with_callable_arity(callable_arity)
15461                .with_callable_parameter_types(callable_parameter_types)
15462                .with_return_type_text(return_type_text)
15463                .with_return_type_identity(return_type_identity),
15464        );
15465    };
15466    let parameters_start = search_from + relative_start;
15467    let parameters_end = parameters_start + parameter_text.len();
15468    let mut search_start = parameters_start;
15469    let parameters = cpp_parameter_label_nodes(parameters_node)
15470        .into_iter()
15471        .filter_map(|label_node| {
15472            let label = normalize_cpp_whitespace(node_text(label_node, source));
15473            if label.is_empty() || search_start > parameters_end {
15474                return None;
15475            }
15476            let haystack = signature.get(search_start..parameters_end)?;
15477            let relative_start = haystack.find(&label)?;
15478            let start_byte = search_start + relative_start;
15479            let end_byte = start_byte + label.len();
15480            search_start = end_byte;
15481            Some(ParameterMetadata::new(label, start_byte, end_byte))
15482        })
15483        .collect();
15484    enrich(
15485        SignatureMetadata::new(signature, parameters)
15486            .with_callable_arity(callable_arity)
15487            .with_callable_parameter_types(callable_parameter_types)
15488            .with_return_type_text(return_type_text)
15489            .with_return_type_identity(return_type_identity),
15490    )
15491}
15492
15493pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15494    function_declarator: Node<'tree>,
15495    source: &str,
15496    ancestry: &ParentIndex<'tree>,
15497) -> bool {
15498    let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15499        .or_else(|| function_declarator.child_by_field_name("name"))
15500        .or_else(|| last_named_child(function_declarator))
15501    else {
15502        return false;
15503    };
15504    // An out-of-line definition writes the class in the name itself
15505    // (`Client::Client()`, `shop::Client::Client()`), so the qualifier's own
15506    // terminal component answers without any enclosing class body.
15507    if name_node.kind() == "qualified_identifier" {
15508        let owner = name_node
15509            .child_by_field_name("scope")
15510            .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15511        let member = name_node
15512            .child_by_field_name("name")
15513            .and_then(|name| canonical_cpp_qualified_component(name, source));
15514        return match (owner, member) {
15515            (Some(owner), Some(member)) => owner.name == member.name,
15516            _ => false,
15517        };
15518    }
15519    let Some(callable_name) = direct_identifier_name(name_node, source) else {
15520        return false;
15521    };
15522
15523    let mut current = ancestry.parent(function_declarator);
15524    while let Some(ancestor) = current {
15525        let owner_name = match ancestor.kind() {
15526            "class_specifier" | "struct_specifier" | "union_specifier" => {
15527                class_like_name(ancestor, source, ancestry)
15528            }
15529            "ERROR" => malformed_class_error_owner_name(ancestor, source),
15530            _ => None,
15531        };
15532        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15533            return true;
15534        }
15535        current = ancestry.parent(ancestor);
15536    }
15537    false
15538}
15539
15540/// Recover the owner name from the direct grammar shape retained when a later
15541/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
15542/// `ERROR` node:
15543///
15544/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
15545///
15546/// Direct-child checks keep this distinct from an unrelated nested class inside
15547/// a broader error region. The closing brace may be displaced past the error
15548/// node, so the opening body token is the available structural boundary.
15549fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15550    if node.kind() != "ERROR" {
15551        return None;
15552    }
15553    let keyword = node.child(0)?;
15554    if !matches!(keyword.kind(), "class" | "struct" | "union") {
15555        return None;
15556    }
15557    let name_node = node.child(1)?;
15558    let name = direct_identifier_name(name_node, source)?;
15559    let has_body = (2..node.child_count())
15560        .filter_map(|index| node.child(index))
15561        .any(|child| child.kind() == "{");
15562    has_body.then_some(name)
15563}
15564
15565/// Whether one declaration carries a native must-use attribute.
15566///
15567/// Attribute names are read only from structurally selected attribute nodes;
15568/// an identifier with the same spelling elsewhere in the declaration cannot
15569/// produce a proof. GNU's spelling is accepted for `warn_unused_result`,
15570/// while standard `nodiscard` is accepted without a namespace prefix.
15571pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15572    // The C++ grammar keeps a prefix attribute on the enclosing declaration
15573    // while the enum unit's occurrence points at its `enum_specifier`. Bring
15574    // that one structural sibling into the same declaration boundary; this
15575    // does not widen the walk to surrounding declarations.
15576    let declaration = declaration
15577        .parent()
15578        .filter(|parent| {
15579            parent.kind() == "declaration"
15580                && parent
15581                    .child_by_field_name("type")
15582                    .is_some_and(|type_node| type_node.id() == declaration.id())
15583        })
15584        .unwrap_or(declaration);
15585    let mut pending = vec![declaration];
15586    while let Some(node) = pending.pop() {
15587        if cpp_attribute_node_is_native_must_use(node, source)
15588            && cpp_attribute_is_attached_to_declaration(node, declaration)
15589        {
15590            return true;
15591        }
15592        if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15593        {
15594            continue;
15595        }
15596        push_named_children_reversed(node, &mut pending);
15597    }
15598    false
15599}
15600
15601fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15602    if node.kind() == "attribute" {
15603        return node.child_by_field_name("name").is_some_and(|name| {
15604            let name = node_text(name, source);
15605            let prefix = node
15606                .child_by_field_name("prefix")
15607                .map(|prefix| node_text(prefix, source));
15608            matches!(
15609                (prefix, name),
15610                (None, "nodiscard")
15611                    | (None, "warn_unused_result")
15612                    | (Some("gnu"), "warn_unused_result")
15613            )
15614        });
15615    }
15616    if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15617    {
15618        return false;
15619    }
15620    let Some(arguments) = node.named_child(0) else {
15621        return false;
15622    };
15623    arguments.kind() == "argument_list"
15624        && arguments.named_children(&mut arguments.walk()).any(|name| {
15625            name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15626        })
15627}
15628
15629fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15630    let mut current = attribute;
15631    while let Some(parent) = current.parent() {
15632        if parent.id() == declaration.id() {
15633            return true;
15634        }
15635        if matches!(
15636            parent.kind(),
15637            "declaration"
15638                | "field_declaration"
15639                | "function_definition"
15640                | "struct_specifier"
15641                | "class_specifier"
15642                | "union_specifier"
15643                | "enum_specifier"
15644        ) {
15645            return false;
15646        }
15647        current = parent;
15648    }
15649    false
15650}
15651
15652/// Whether export/decorator macros ahead of a callable declaration's `type`
15653/// make that type head decoration rather than a semantic return type. The
15654/// older grammar leaves the macro token in the `type` field and the real type
15655/// in an ERROR sibling; the macro-declaration grammar wraps the same token as a
15656/// leading `macro_modifier`, or the whole invocation as a
15657/// `macro_type_specifier` type. Such a declaration does not carry enough
15658/// structured evidence to persist a return type, as text or as identity.
15659fn cpp_declaration_return_type_is_macro_decorated(
15660    declaration: Node<'_>,
15661    type_node: Node<'_>,
15662    source: &str,
15663) -> bool {
15664    type_node.kind() == "macro_type_specifier"
15665        || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15666        || (cpp_export_macro_token(node_text(type_node, source))
15667            && (0..declaration.named_child_count()).any(|index| {
15668                declaration
15669                    .named_child(index)
15670                    .is_some_and(|child| child.kind() == "ERROR")
15671            }))
15672}
15673
15674/// Whether a `macro_modifier` precedes `type_node` among `declaration`'s
15675/// specifiers: the macro-declaration grammar's clean reading of an unexpanded
15676/// decorator macro ahead of the declared type (`MACRO static inline T f()`).
15677pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15678    declaration: Node<'_>,
15679    type_node: Node<'_>,
15680) -> bool {
15681    let mut head_cursor = declaration.walk();
15682    declaration
15683        .named_children(&mut head_cursor)
15684        .take_while(|child| *child != type_node)
15685        .any(|child| child.kind() == "macro_modifier")
15686}
15687
15688fn cpp_callable_return_type_parts<'tree>(
15689    function_declarator: Node<'tree>,
15690    source: &str,
15691    ancestry: &ParentIndex<'tree>,
15692) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15693    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15694        return None;
15695    }
15696    if let Some((return_type, _)) =
15697        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15698    {
15699        return Some((return_type, Vec::new()));
15700    }
15701    let mut cursor = function_declarator.walk();
15702    if let Some(trailing) = function_declarator
15703        .named_children(&mut cursor)
15704        .find(|child| child.kind() == "trailing_return_type")
15705        && let Some(type_descriptor) = trailing.named_child(0)
15706    {
15707        return Some((type_descriptor, Vec::new()));
15708    }
15709
15710    let mut current = function_declarator;
15711    let mut wrappers = Vec::new();
15712    while let Some(parent) = ancestry.parent(current) {
15713        if matches!(
15714            parent.kind(),
15715            "function_definition" | "declaration" | "field_declaration"
15716        ) {
15717            let type_node = parent.child_by_field_name("type")?;
15718            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15719                return None;
15720            }
15721            return Some((type_node, wrappers));
15722        }
15723        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15724            || (matches!(
15725                parent.kind(),
15726                "pointer_declarator"
15727                    | "reference_declarator"
15728                    | "array_declarator"
15729                    | "parenthesized_declarator"
15730            ) && parent.named_child_count() == 1
15731                && parent.named_child(0) == Some(current));
15732        if !wraps_current_declarator {
15733            return None;
15734        }
15735        match parent.kind() {
15736            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15737            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15738            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15739            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15740            _ => return None,
15741        }
15742        current = parent;
15743    }
15744    None
15745}
15746
15747/// Return the AST node that describes one callable's declared result type.
15748///
15749/// The node is returned only after the same declarator walk that establishes
15750/// the persisted C++ return identity. Consumers can therefore resolve a
15751/// nominal result or inspect its declaration attributes without parsing a
15752/// rendered signature.
15753pub fn cpp_callable_declaration_return_type_node<'tree>(
15754    callable: Node<'tree>,
15755    source: &str,
15756    ancestry: &ParentIndex<'tree>,
15757) -> Option<Node<'tree>> {
15758    let declarator = callable
15759        .child_by_field_name("declarator")
15760        .and_then(extract_function_declarator)?;
15761    cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15762}
15763
15764/// Return the structured declared result type of one callable declaration.
15765///
15766/// This accepts the enclosing declaration node so consumers do not need to
15767/// duplicate the declarator-unwrapping rules before asking for the structured
15768/// identity.
15769pub fn cpp_callable_declaration_return_type_identity<'tree>(
15770    callable: Node<'tree>,
15771    source: &str,
15772    ancestry: &ParentIndex<'tree>,
15773) -> Option<StructuredTypeIdentity> {
15774    let declarator = callable
15775        .child_by_field_name("declarator")
15776        .and_then(extract_function_declarator)?;
15777    cpp_callable_return_type_identity(declarator, source, ancestry)
15778}
15779
15780pub(crate) fn cpp_callable_return_type_identity<'tree>(
15781    function_declarator: Node<'tree>,
15782    source: &str,
15783    ancestry: &ParentIndex<'tree>,
15784) -> Option<StructuredTypeIdentity> {
15785    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15786    let (return_type, wrappers) =
15787        cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15788    cpp_apply_structured_type_wrappers(
15789        cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15790        wrappers,
15791    )
15792}
15793
15794fn cpp_structured_type_identity(
15795    node: Node<'_>,
15796    source: &str,
15797    lexical_scope: &[String],
15798) -> Option<StructuredTypeIdentity> {
15799    enum Work<'tree> {
15800        Visit(Node<'tree>),
15801        Wrap(CppStructuredTypeWrapper),
15802        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15803        BuildGeneric { argument_count: usize },
15804    }
15805
15806    let mut work = vec![Work::Visit(node)];
15807    let mut values = Vec::new();
15808    let mut builder = StructuredTypeIdentityBuilder::default();
15809    while let Some(next) = work.pop() {
15810        match next {
15811            Work::Visit(current) => match current.kind() {
15812                "type_descriptor" => {
15813                    let type_node = current
15814                        .child_by_field_name("type")
15815                        .or_else(|| current.named_child(0))?;
15816                    let mut wrappers = Vec::new();
15817                    let mut cursor = current.walk();
15818                    for child in current.named_children(&mut cursor) {
15819                        if child.id() != type_node.id() {
15820                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15821                        }
15822                    }
15823                    work.push(Work::ApplyWrappers(wrappers));
15824                    work.push(Work::Visit(type_node));
15825                }
15826                "pointer_declarator" | "abstract_pointer_declarator" => {
15827                    let child = current
15828                        .child_by_field_name("declarator")
15829                        .or_else(|| current.named_child(0))?;
15830                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15831                    work.push(Work::Visit(child));
15832                }
15833                "reference_declarator" => {
15834                    let child = current
15835                        .child_by_field_name("declarator")
15836                        .or_else(|| current.named_child(0))?;
15837                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15838                    work.push(Work::Visit(child));
15839                }
15840                "array_declarator" | "abstract_array_declarator" => {
15841                    let child = current
15842                        .child_by_field_name("declarator")
15843                        .or_else(|| current.named_child(0))?;
15844                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15845                    work.push(Work::Visit(child));
15846                }
15847                "template_type" => {
15848                    let name_node = current.child_by_field_name("name")?;
15849                    let arguments = current
15850                        .child_by_field_name("arguments")
15851                        .map(|arguments_node| {
15852                            let mut cursor = arguments_node.walk();
15853                            arguments_node
15854                                .named_children(&mut cursor)
15855                                .filter(|child| !child.is_extra() && child.kind() != "comment")
15856                                .collect::<Vec<_>>()
15857                        })
15858                        .unwrap_or_default();
15859                    work.push(Work::BuildGeneric {
15860                        argument_count: arguments.len(),
15861                    });
15862                    work.extend(arguments.into_iter().rev().map(Work::Visit));
15863                    work.push(Work::Visit(name_node));
15864                }
15865                "qualified_identifier"
15866                | "scoped_identifier"
15867                | "scoped_type_identifier"
15868                | "type_identifier"
15869                | "field_identifier"
15870                | "identifier"
15871                | "namespace_identifier"
15872                | "primitive_type" => {
15873                    values.push(builder.named(cpp_structured_named_type(
15874                        current,
15875                        source,
15876                        lexical_scope,
15877                    )?)?);
15878                }
15879                _ => {
15880                    let child = current.child_by_field_name("type").or_else(|| {
15881                        (current.named_child_count() == 1)
15882                            .then(|| current.named_child(0))
15883                            .flatten()
15884                    })?;
15885                    work.push(Work::Visit(child));
15886                }
15887            },
15888            Work::Wrap(wrapper) => {
15889                let root = values.pop()?;
15890                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15891            }
15892            Work::ApplyWrappers(wrappers) => {
15893                let mut root = values.pop()?;
15894                for wrapper in wrappers.into_iter().rev() {
15895                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15896                }
15897                values.push(root);
15898            }
15899            Work::BuildGeneric { argument_count } => {
15900                let value_count = argument_count.checked_add(1)?;
15901                let start = values.len().checked_sub(value_count)?;
15902                let mut built = values.split_off(start);
15903                let base = built.remove(0);
15904                values.push(builder.generic(base, built)?);
15905            }
15906        }
15907    }
15908    (values.len() == 1)
15909        .then(|| values.pop())
15910        .flatten()
15911        .and_then(|root| builder.finish(root))
15912}
15913
15914fn cpp_structured_named_type(
15915    node: Node<'_>,
15916    source: &str,
15917    lexical_scope: &[String],
15918) -> Option<StructuredTypeName> {
15919    let path = cpp_structured_type_path(node, source)?;
15920    let absolute = node.child_by_field_name("scope").is_none()
15921        && node.child(0).is_some_and(|child| child.kind() == "::");
15922    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15923}
15924
15925#[derive(Clone, Copy)]
15926enum CppStructuredTypeWrapper {
15927    Pointer,
15928    LvalueReference,
15929    RvalueReference,
15930    Array,
15931}
15932
15933fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15934    let mut wrappers = Vec::new();
15935    let mut current = node;
15936    loop {
15937        match current.kind() {
15938            "pointer_declarator" | "abstract_pointer_declarator" => {
15939                wrappers.push(CppStructuredTypeWrapper::Pointer)
15940            }
15941            "reference_declarator" | "abstract_reference_declarator" => {
15942                wrappers.push(cpp_reference_wrapper(current)?);
15943            }
15944            "array_declarator" | "abstract_array_declarator" => {
15945                wrappers.push(CppStructuredTypeWrapper::Array)
15946            }
15947            // An initializer and a grouping parenthesis are not type
15948            // structure. Stopping at one of them dropped every wrapper an
15949            // initialized or parenthesized declarator writes, so
15950            // `std::string &alias = value;` reported the unqualified
15951            // `std::string` while `const std::string &value` as a parameter
15952            // reported the reference it shares.
15953            "init_declarator" | "parenthesized_declarator" => {}
15954            _ => break,
15955        }
15956        let Some(child) = current
15957            .child_by_field_name("declarator")
15958            .or_else(|| current.named_child(0))
15959        else {
15960            break;
15961        };
15962        current = child;
15963    }
15964    Some(wrappers)
15965}
15966
15967fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15968    node.children(&mut node.walk())
15969        .find_map(|child| match child.kind() {
15970            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15971            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15972            _ => None,
15973        })
15974}
15975
15976fn cpp_wrap_structured_type(
15977    identity: StructuredTypeIdentity,
15978    wrapper: CppStructuredTypeWrapper,
15979) -> Option<StructuredTypeIdentity> {
15980    match wrapper {
15981        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15982        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15983        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15984        CppStructuredTypeWrapper::Array => identity.wrap_array(),
15985    }
15986}
15987
15988fn cpp_apply_structured_type_wrappers(
15989    mut identity: StructuredTypeIdentity,
15990    wrappers: Vec<CppStructuredTypeWrapper>,
15991) -> Option<StructuredTypeIdentity> {
15992    for wrapper in wrappers.into_iter().rev() {
15993        identity = cpp_wrap_structured_type(identity, wrapper)?;
15994    }
15995    Some(identity)
15996}
15997
15998fn cpp_wrap_structured_type_node(
15999    builder: &mut StructuredTypeIdentityBuilder,
16000    inner: StructuredTypeNodeId,
16001    wrapper: CppStructuredTypeWrapper,
16002) -> Option<StructuredTypeNodeId> {
16003    match wrapper {
16004        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
16005        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
16006        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
16007        CppStructuredTypeWrapper::Array => builder.array(inner),
16008    }
16009}
16010
16011/// The dot-free name components a qualified C++ name node writes, outermost
16012/// first.
16013///
16014/// The walk reads the grammar's `scope` and `name` fields rather than cutting
16015/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
16016/// shape the grammar does not name yields nothing at all.
16017pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
16018    let mut path = Vec::new();
16019    let mut stack = vec![node];
16020    while let Some(current) = stack.pop() {
16021        match current.kind() {
16022            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
16023                let component = node_text(current, source).to_string();
16024                if component.is_empty() {
16025                    return None;
16026                }
16027                path.push(component);
16028            }
16029            "template_type" | "dependent_type" => {
16030                stack.push(current.child_by_field_name("name")?);
16031            }
16032            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16033                stack.push(current.child_by_field_name("name")?);
16034                if let Some(scope) = current.child_by_field_name("scope") {
16035                    stack.push(scope);
16036                }
16037            }
16038            _ => return None,
16039        }
16040    }
16041    (!path.is_empty()).then_some(path)
16042}
16043
16044fn cpp_callable_lexical_scope<'tree>(
16045    node: Node<'tree>,
16046    source: &str,
16047    ancestry: &ParentIndex<'tree>,
16048) -> Vec<String> {
16049    let mut groups = Vec::new();
16050    let mut current = ancestry.parent(node);
16051    while let Some(parent) = current {
16052        if matches!(
16053            parent.kind(),
16054            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
16055        ) && let Some(name_node) = parent.child_by_field_name("name")
16056            && let Some(components) = cpp_structured_type_path(name_node, source)
16057            && !components.is_empty()
16058        {
16059            groups.push(components);
16060        }
16061        current = ancestry.parent(parent);
16062    }
16063    groups.reverse();
16064    groups.into_iter().flatten().collect()
16065}
16066
16067/// Whether a call on this callable's static target can select another body.
16068///
16069/// C++ says this where the member is *declared*, not where it is defined, and
16070/// that is the whole difficulty of the question: virtual-ness is inherited
16071/// (`struct Derived : Base { void run(); }` makes `Derived::run` virtual when
16072/// `Base::run` is), an out-of-line definition repeats none of it, and whether
16073/// a definition's qualifier names a class or a namespace is a class-table
16074/// question. So a row answers `Closed` only when its own node proves it:
16075///
16076/// * a written `final` is the one specifier that ends the family here, and it
16077///   is the only thing that closes a member of a class that has a base clause;
16078/// * a class-body member is `Closed` otherwise only when its class body writes
16079///   no base clause at all, because a base clause is the only evidence in this
16080///   file that some base may declare this member virtual;
16081/// * anything with a virtual-family boundary, a template head, or a
16082///   preprocessor or error region is `Open`;
16083/// * a qualified declaration outside every class body answers `None`: its
16084///   class body, which is where the fact is written, may be in another file.
16085///
16086/// The analyzer joins the `None` rows with the include-visible class-body rows
16087/// of the same logical callable (#3507), the same join #3509 makes for a
16088/// definition's declared access; a row that keeps `None` after that join stays
16089/// unknown and no dispatch proof may be built on it.
16090fn cpp_callable_dispatch_extensibility<'tree>(
16091    function_declarator: Node<'tree>,
16092    ancestry: &ParentIndex<'tree>,
16093) -> Option<DispatchExtensibility> {
16094    let mut declaration = None;
16095    // The first class body this declarator is written in. The upward walk
16096    // visits it anyway, so only the one step from the body to its class node
16097    // is extra, and only for a class-body member.
16098    let mut class_body = None;
16099    let mut current = Some(function_declarator);
16100    while let Some(node) = current {
16101        match node.kind() {
16102            "template_declaration"
16103            | "preproc_if"
16104            | "preproc_ifdef"
16105            | "preproc_else"
16106            | "preproc_elif"
16107            | "preproc_call"
16108            | "ERROR" => return Some(DispatchExtensibility::Open),
16109            "declaration" | "field_declaration" | "function_definition" => {
16110                declaration.get_or_insert(node);
16111            }
16112            "field_declaration_list" => {
16113                class_body.get_or_insert(node);
16114            }
16115            "translation_unit" => break,
16116            _ => {}
16117        }
16118        current = ancestry.parent(node);
16119    }
16120    let Some(declaration) = declaration else {
16121        return Some(DispatchExtensibility::Open);
16122    };
16123
16124    let mut saw_virtual_boundary = false;
16125    let mut stack = vec![declaration];
16126    while let Some(node) = stack.pop() {
16127        match node.kind() {
16128            "compound_statement" | "field_declaration_list" => continue,
16129            "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16130            "virtual"
16131            | "override"
16132            | "virtual_specifier"
16133            | "pure_virtual_clause"
16134            | "template_parameter_list"
16135            | "template_method"
16136            | "template_function"
16137            | "ERROR" => saw_virtual_boundary = true,
16138            _ => {}
16139        }
16140        let mut cursor = node.walk();
16141        stack.extend(node.children(&mut cursor));
16142    }
16143
16144    if saw_virtual_boundary {
16145        return Some(DispatchExtensibility::Open);
16146    }
16147    if let Some(body) = class_body {
16148        // C++ inherits virtual-ness, so a member written without a `virtual`
16149        // keyword is still virtual when a base declares it. Whether some base
16150        // does is not decidable here, so a base clause keeps the answer open.
16151        let has_base_clause = ancestry
16152            .parent(body)
16153            .and_then(cpp_class_like_base_clause)
16154            .is_some();
16155        return Some(if has_base_clause {
16156            DispatchExtensibility::Open
16157        } else {
16158            DispatchExtensibility::Closed
16159        });
16160    }
16161    if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16162        return None;
16163    }
16164    // A namespace or file scope callable is reached by name, never through a
16165    // receiver, so no other body can be selected for a call to it.
16166    Some(DispatchExtensibility::Closed)
16167}
16168
16169fn cpp_callable_linkage<'tree>(
16170    declaration: Node<'tree>,
16171    source: &str,
16172    ancestry: &ParentIndex<'tree>,
16173) -> CallableLinkage {
16174    let mut enclosed_by_class = false;
16175    let mut current = ancestry.parent(declaration);
16176    while let Some(node) = current {
16177        if node.kind() == "namespace_definition"
16178            && node
16179                .child_by_field_name("name")
16180                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16181        {
16182            return CallableLinkage::Internal;
16183        }
16184        if matches!(
16185            node.kind(),
16186            "class_specifier" | "struct_specifier" | "union_specifier"
16187        ) {
16188            if node
16189                .child_by_field_name("name")
16190                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16191            {
16192                return CallableLinkage::Internal;
16193            }
16194            enclosed_by_class = true;
16195        }
16196        // An export macro between `class` and the class name can make
16197        // tree-sitter parse the entire class as a function definition. The
16198        // structured recovery proves that this container is a named class
16199        // scope, not a local callable scope.
16200        if node.kind() == "lambda_expression"
16201            || node.kind() == "function_definition"
16202                && !is_recovered_exported_class_container(node, source)
16203        {
16204            return CallableLinkage::Internal;
16205        }
16206        current = ancestry.parent(node);
16207    }
16208
16209    if enclosed_by_class {
16210        return CallableLinkage::External;
16211    }
16212
16213    if cpp_declares_static_storage_class(declaration, source) {
16214        CallableLinkage::Internal
16215    } else {
16216        CallableLinkage::External
16217    }
16218}
16219
16220fn cpp_callable_return_type_text<'tree>(
16221    function_declarator: Node<'tree>,
16222    source: &str,
16223    ancestry: &ParentIndex<'tree>,
16224) -> Option<String> {
16225    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16226        return None;
16227    }
16228    if let Some((return_type, _)) =
16229        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16230    {
16231        let text = normalize_cpp_whitespace(node_text(return_type, source));
16232        return (!text.is_empty()).then_some(text);
16233    }
16234    let mut cursor = function_declarator.walk();
16235    if let Some(trailing) = function_declarator
16236        .named_children(&mut cursor)
16237        .find(|child| child.kind() == "trailing_return_type")
16238        && let Some(type_descriptor) = trailing.named_child(0)
16239    {
16240        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16241        if !text.is_empty() {
16242            return Some(text);
16243        }
16244    }
16245
16246    let mut current = function_declarator;
16247    let mut indirection = String::new();
16248    while let Some(parent) = ancestry.parent(current) {
16249        if matches!(
16250            parent.kind(),
16251            "function_definition" | "declaration" | "field_declaration"
16252        ) {
16253            let type_node = parent.child_by_field_name("type")?;
16254            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16255                return None;
16256            }
16257            let base = normalize_cpp_whitespace(node_text(type_node, source));
16258            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16259        }
16260        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16261            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16262                && parent.named_child_count() == 1
16263                && parent.named_child(0) == Some(current));
16264        if wraps_current_declarator {
16265            match parent.kind() {
16266                "pointer_declarator" => indirection.push('*'),
16267                "reference_declarator" => {
16268                    let reference = parent
16269                        .children(&mut parent.walk())
16270                        .find(|child| !child.is_named())
16271                        .map(|child| node_text(child, source))
16272                        .unwrap_or("&");
16273                    indirection.push_str(reference);
16274                }
16275                "init_declarator" | "parenthesized_declarator" => {}
16276                _ => return None,
16277            }
16278            current = parent;
16279            continue;
16280        }
16281        return None;
16282    }
16283    None
16284}
16285
16286pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
16287    let mut required = 0;
16288    let mut total = 0;
16289    let mut repeated = false;
16290    for slot in cpp_callable_parameter_slots(parameters_node, source) {
16291        match slot {
16292            CppParameterSlot::Declared {
16293                required: is_required,
16294                ..
16295            } => {
16296                total += 1;
16297                if is_required {
16298                    required += 1;
16299                }
16300            }
16301            CppParameterSlot::OptionalVoid => total += 1,
16302            CppParameterSlot::Ellipsis => repeated = true,
16303        }
16304    }
16305    CallableArity::new(required, total, repeated)
16306}
16307
16308/// One entry of a callable's invocation parameter list, plus an optional
16309/// unnamed `void` declaration retained for the legacy arity result.
16310///
16311/// The list excludes an explicit object parameter and a lone `void`. Every
16312/// derivation of parameter arity or type starts from this same sequence, so
16313/// those views cannot disagree about which parameters exist.
16314#[derive(Clone, Copy)]
16315enum CppParameterSlot<'tree> {
16316    Declared {
16317        parameter: Node<'tree>,
16318        required: bool,
16319    },
16320    OptionalVoid,
16321    Ellipsis,
16322}
16323
16324fn cpp_callable_parameter_slots<'tree>(
16325    parameters_node: Node<'tree>,
16326    source: &str,
16327) -> Vec<CppParameterSlot<'tree>> {
16328    let mut slots = Vec::new();
16329    let mut cursor = parameters_node.walk();
16330    for parameter in parameters_node.children(&mut cursor) {
16331        match parameter.kind() {
16332            "parameter_declaration" | "optional_parameter_declaration" => {
16333                let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16334                    && parameter
16335                        .child_by_field_name("type")
16336                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16337                if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16338                    continue;
16339                }
16340                if is_unnamed_void {
16341                    slots.push(CppParameterSlot::OptionalVoid);
16342                    continue;
16343                }
16344                slots.push(CppParameterSlot::Declared {
16345                    parameter,
16346                    required: parameter.kind() == "parameter_declaration",
16347                });
16348            }
16349            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16350                slots.push(CppParameterSlot::Ellipsis);
16351            }
16352            _ => {}
16353        }
16354    }
16355    slots
16356}
16357
16358pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16359    cpp_callable_parameter_slots(parameters_node, source)
16360        .into_iter()
16361        .filter_map(|slot| match slot {
16362            CppParameterSlot::Declared { parameter, .. } => {
16363                Some(cpp_parameter_type(parameter, source))
16364            }
16365            CppParameterSlot::Ellipsis => Some("...".to_string()),
16366            CppParameterSlot::OptionalVoid => None,
16367        })
16368        .collect()
16369}
16370
16371/// Return the written name of each callable parameter in invocation order.
16372/// C and C++ permit declarations without names; those slots deliberately stay
16373/// `None` so consumers can distinguish an unnamed formal from a guessed one.
16374pub(crate) fn cpp_callable_parameter_names(
16375    parameters_node: Node<'_>,
16376    source: &str,
16377) -> Vec<Option<String>> {
16378    cpp_callable_parameter_slots(parameters_node, source)
16379        .into_iter()
16380        .filter_map(|slot| match slot {
16381            CppParameterSlot::Declared { parameter, .. } => Some(
16382                parameter
16383                    .child_by_field_name("declarator")
16384                    .and_then(crate::structural::declarator_name_node)
16385                    .and_then(|name| direct_identifier_name(name, source)),
16386            ),
16387            CppParameterSlot::Ellipsis => Some(None),
16388            CppParameterSlot::OptionalVoid => None,
16389        })
16390        .collect()
16391}
16392
16393/// One callable parameter's parser-derived type.
16394///
16395/// A rendered spelling such as `const T&` is a source text, not a type name. A
16396/// consumer that must publish a type into a structured model - a semantic-pack
16397/// type reference, for example - reads this instead.
16398#[derive(Debug, Clone, PartialEq, Eq)]
16399pub enum CppParameterType {
16400    /// The written type reduced to a structured identity. C++ cv-qualifiers
16401    /// have no place in that model and are not represented.
16402    Structured(StructuredTypeIdentity),
16403    /// A `...` pack, which declares no parameter type at all.
16404    Ellipsis,
16405    /// A written type with no structured reduction, such as a macro-obscured,
16406    /// `decltype`-computed, or function-pointer parameter.
16407    Unstructured,
16408}
16409
16410/// The structured type of each invocation parameter, in declaration order.
16411///
16412/// The result is index-parallel with the rendered
16413/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
16414/// callable.
16415pub fn cpp_callable_parameter_type_identities<'tree>(
16416    function_declarator: Node<'tree>,
16417    source: &str,
16418    ancestry: &ParentIndex<'tree>,
16419) -> Vec<CppParameterType> {
16420    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16421        return Vec::new();
16422    };
16423    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16424    cpp_callable_parameter_slots(parameters_node, source)
16425        .into_iter()
16426        .filter_map(|slot| match slot {
16427            CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16428            CppParameterSlot::Declared { parameter, .. } => Some(
16429                cpp_parameter_type_identity(parameter, source, &lexical_scope)
16430                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16431            ),
16432            CppParameterSlot::OptionalVoid => None,
16433        })
16434        .collect()
16435}
16436
16437/// The parser-derived type of one declared object or parameter.
16438///
16439/// The declaration owns the base `type` field while the individual declarator
16440/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
16441/// lets callers distinguish several declarators in one declaration without
16442/// reparsing a rendered signature.
16443pub fn cpp_declaration_type_identity<'tree>(
16444    declaration: Node<'tree>,
16445    declarator: Node<'tree>,
16446    source: &str,
16447    ancestry: &ParentIndex<'tree>,
16448) -> Option<StructuredTypeIdentity> {
16449    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16450    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16451}
16452
16453fn cpp_parameter_type_identity(
16454    parameter: Node<'_>,
16455    source: &str,
16456    lexical_scope: &[String],
16457) -> Option<StructuredTypeIdentity> {
16458    cpp_declaration_type_identity_in_scope(
16459        parameter,
16460        cpp_parameter_declarator(parameter),
16461        source,
16462        lexical_scope,
16463    )
16464}
16465
16466fn cpp_declaration_type_identity_in_scope(
16467    declaration: Node<'_>,
16468    declarator: Option<Node<'_>>,
16469    source: &str,
16470    lexical_scope: &[String],
16471) -> Option<StructuredTypeIdentity> {
16472    let type_node = declaration.child_by_field_name("type")?;
16473    let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16474    if let Some(declarator) = declarator {
16475        return cpp_apply_structured_type_wrappers(
16476            identity,
16477            cpp_structured_declarator_wrappers(declarator)?,
16478        );
16479    }
16480    Some(identity)
16481}
16482
16483/// The comparable shape of each invocation parameter, in declaration order.
16484///
16485/// The result is index-parallel with
16486/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
16487/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
16488/// must treat as evidence of nothing rather than as agreement.
16489pub fn cpp_comparable_parameter_shapes<'tree>(
16490    function_declarator: Node<'tree>,
16491    source: &str,
16492    ancestry: &ParentIndex<'tree>,
16493) -> Vec<CppComparableSlot> {
16494    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16495        return Vec::new();
16496    };
16497    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16498    cpp_callable_parameter_slots(parameters_node, source)
16499        .into_iter()
16500        .filter_map(|slot| match slot {
16501            CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16502            CppParameterSlot::Declared { parameter, .. } => Some(
16503                cpp_comparable_parameter(parameter, source, &lexical_scope)
16504                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16505            ),
16506            CppParameterSlot::OptionalVoid => None,
16507        })
16508        .collect()
16509}
16510
16511fn cpp_comparable_parameter(
16512    parameter: Node<'_>,
16513    source: &str,
16514    lexical_scope: &[String],
16515) -> Option<CppComparableParameter> {
16516    let type_node = parameter.child_by_field_name("type")?;
16517    let levels = match cpp_parameter_declarator(parameter) {
16518        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16519        None => Vec::new(),
16520    };
16521    let mut shape = cpp_comparable_type_shape(
16522        type_node,
16523        cpp_cv_qualifiers(parameter, source),
16524        levels,
16525        source,
16526        lexical_scope,
16527    )?;
16528    shape.adjust_parameter_top_level();
16529    Some(shape)
16530}
16531
16532/// The `const` and `volatile` qualifiers written as direct named children of
16533/// `node`.
16534///
16535/// The grammar exposes `type_qualifier` as a non-field named child in exactly
16536/// the three places a parameter's qualifiers can be written: on the
16537/// `parameter_declaration` itself (the base type), on a `type_descriptor`
16538/// (inside a template argument list), and on each `pointer_declarator` level
16539/// (the pointer object). Every other qualifier the grammar admits - `restrict`
16540/// and friends - takes no part in C++ type identity, the same filter
16541/// `cpp_parameter_type` applies to the rendered spelling (#1827).
16542fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16543    let mut qualifiers = CppCvQualifiers::default();
16544    let mut cursor = node.walk();
16545    for child in node.named_children(&mut cursor) {
16546        if child.kind() != "type_qualifier" {
16547            continue;
16548        }
16549        match node_text(child, source) {
16550            "const" => qualifiers.konst = true,
16551            "volatile" => qualifiers.volatil = true,
16552            _ => {}
16553        }
16554    }
16555    qualifiers
16556}
16557
16558#[derive(Clone, Copy, Default)]
16559struct CppCvQualifiers {
16560    konst: bool,
16561    volatil: bool,
16562}
16563
16564impl CppCvQualifiers {
16565    fn union(self, other: Self) -> Self {
16566        Self {
16567            konst: self.konst || other.konst,
16568            volatil: self.volatil || other.volatil,
16569        }
16570    }
16571}
16572
16573/// One pointer, reference or array level a declarator chain adds.
16574#[derive(Clone, Copy)]
16575enum CppComparableLevel {
16576    Pointer { konst: bool, volatil: bool },
16577    Reference,
16578    Array,
16579}
16580
16581/// The levels `declarator` adds, outermost written level first.
16582///
16583/// C++ declarator syntax binds inside out: the level written closest to the
16584/// declared name is the outermost type constructor, and tree-sitter nests it
16585/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
16586/// applies in order to reach "array of pointer to int", and the qualifier of
16587/// `int * const *p` is read on the level it was written next to, the inner
16588/// pointer of the resulting type.
16589///
16590/// A declarator chain that names a function type - a function-pointer
16591/// parameter - has no comparable shape and reports `None`, matching the
16592/// structured identity channel.
16593fn cpp_comparable_declarator_levels(
16594    declarator: Node<'_>,
16595    source: &str,
16596) -> Option<Vec<CppComparableLevel>> {
16597    let mut levels = Vec::new();
16598    let mut current = declarator;
16599    loop {
16600        match current.kind() {
16601            "pointer_declarator" | "abstract_pointer_declarator" => {
16602                let qualifiers = cpp_cv_qualifiers(current, source);
16603                levels.push(CppComparableLevel::Pointer {
16604                    konst: qualifiers.konst,
16605                    volatil: qualifiers.volatil,
16606                });
16607            }
16608            "reference_declarator" | "abstract_reference_declarator" => {
16609                levels.push(CppComparableLevel::Reference);
16610            }
16611            "array_declarator" | "abstract_array_declarator" => {
16612                levels.push(CppComparableLevel::Array);
16613            }
16614            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16615            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16616            _ => return None,
16617        }
16618        let Some(next) = cpp_nested_declarator(current) else {
16619            return Some(levels);
16620        };
16621        current = next;
16622    }
16623}
16624
16625/// Reduce one written type to a comparable arena.
16626///
16627/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
16628/// two additions: each visited type node carries the cv-qualifiers written on
16629/// it, and declarator levels arrive as a prepared list rather than being
16630/// rediscovered inside the walk.
16631fn cpp_comparable_type_shape(
16632    type_node: Node<'_>,
16633    qualifiers: CppCvQualifiers,
16634    levels: Vec<CppComparableLevel>,
16635    source: &str,
16636    lexical_scope: &[String],
16637) -> Option<CppComparableParameter> {
16638    enum Work<'tree> {
16639        Visit {
16640            node: Node<'tree>,
16641            qualifiers: CppCvQualifiers,
16642        },
16643        ApplyLevels(Vec<CppComparableLevel>),
16644        BuildGeneric {
16645            argument_count: usize,
16646        },
16647    }
16648
16649    let mut nodes: Vec<CppComparableNode> = Vec::new();
16650    let mut values: Vec<usize> = Vec::new();
16651    let mut work = vec![
16652        Work::ApplyLevels(levels),
16653        Work::Visit {
16654            node: type_node,
16655            qualifiers,
16656        },
16657    ];
16658    while let Some(next) = work.pop() {
16659        match next {
16660            Work::Visit { node, qualifiers } => match node.kind() {
16661                "type_descriptor" => {
16662                    let inner_type = node
16663                        .child_by_field_name("type")
16664                        .or_else(|| node.named_child(0))?;
16665                    let mut cursor = node.walk();
16666                    let declarator = node.child_by_field_name("declarator").or_else(|| {
16667                        node.named_children(&mut cursor).find(|child| {
16668                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
16669                        })
16670                    });
16671                    let levels = match declarator {
16672                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16673                        None => Vec::new(),
16674                    };
16675                    work.push(Work::ApplyLevels(levels));
16676                    work.push(Work::Visit {
16677                        node: inner_type,
16678                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16679                    });
16680                }
16681                "sized_type_specifier" => {
16682                    // `unsigned char` is one primitive type whose components are
16683                    // partly unnamed tokens, so the whole specifier is its own
16684                    // name component. Reducing it to the `type` child would make
16685                    // `f(unsigned char)` and `f(char)` compare equal.
16686                    let name = StructuredTypeName::new(
16687                        vec![normalize_cpp_whitespace(node_text(node, source))],
16688                        lexical_scope.to_vec(),
16689                        false,
16690                    )?;
16691                    values.push(cpp_push_comparable_node(
16692                        &mut nodes,
16693                        CppComparableNode::Named {
16694                            name,
16695                            primitive: true,
16696                            konst: qualifiers.konst,
16697                            volatil: qualifiers.volatil,
16698                        },
16699                    ));
16700                }
16701                "qualified_identifier"
16702                | "scoped_identifier"
16703                | "scoped_type_identifier"
16704                | "type_identifier"
16705                | "field_identifier"
16706                | "identifier"
16707                | "namespace_identifier"
16708                | "primitive_type"
16709                | "template_type" => {
16710                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
16711                    values.push(cpp_push_comparable_node(
16712                        &mut nodes,
16713                        CppComparableNode::Named {
16714                            name,
16715                            primitive: node.kind() == "primitive_type",
16716                            konst: qualifiers.konst,
16717                            volatil: qualifiers.volatil,
16718                        },
16719                    ));
16720                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16721                        let mut cursor = arguments_node.walk();
16722                        let arguments = arguments_node
16723                            .named_children(&mut cursor)
16724                            .filter(|child| !child.is_extra() && child.kind() != "comment")
16725                            .collect::<Vec<_>>();
16726                        work.push(Work::BuildGeneric {
16727                            argument_count: arguments.len(),
16728                        });
16729                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16730                            node: argument,
16731                            qualifiers: CppCvQualifiers::default(),
16732                        }));
16733                    }
16734                }
16735                _ => {
16736                    let inner = node.child_by_field_name("type").or_else(|| {
16737                        (node.named_child_count() == 1)
16738                            .then(|| node.named_child(0))
16739                            .flatten()
16740                    })?;
16741                    work.push(Work::Visit {
16742                        node: inner,
16743                        qualifiers,
16744                    });
16745                }
16746            },
16747            Work::ApplyLevels(levels) => {
16748                let mut root = values.pop()?;
16749                for level in levels {
16750                    let node = match level {
16751                        CppComparableLevel::Pointer { konst, volatil } => {
16752                            CppComparableNode::Pointer {
16753                                inner: root,
16754                                konst,
16755                                volatil,
16756                            }
16757                        }
16758                        CppComparableLevel::Reference => {
16759                            CppComparableNode::Reference { inner: root }
16760                        }
16761                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16762                    };
16763                    root = cpp_push_comparable_node(&mut nodes, node);
16764                }
16765                values.push(root);
16766            }
16767            Work::BuildGeneric { argument_count } => {
16768                let value_count = argument_count.checked_add(1)?;
16769                let start = values.len().checked_sub(value_count)?;
16770                let mut built = values.split_off(start);
16771                let base = built.remove(0);
16772                values.push(cpp_push_comparable_node(
16773                    &mut nodes,
16774                    CppComparableNode::Generic {
16775                        base,
16776                        arguments: built,
16777                    },
16778                ));
16779            }
16780        }
16781    }
16782    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16783    debug_assert_eq!(
16784        root,
16785        nodes.len().saturating_sub(1),
16786        "comparable nodes are appended in post-order, so the root is the last one"
16787    );
16788    Some(CppComparableParameter::new(nodes, root))
16789}
16790
16791fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16792    nodes.push(node);
16793    nodes.len() - 1
16794}
16795
16796/// The template argument list of the name `node` terminates in, if any.
16797///
16798/// `std::vector<int>` writes its arguments on the `name` of a qualified
16799/// identifier, so a walk that stopped at the qualified node would reduce
16800/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
16801fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16802    let mut current = node;
16803    loop {
16804        match current.kind() {
16805            "template_type" => return current.child_by_field_name("arguments"),
16806            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16807                current = current.child_by_field_name("name")?;
16808            }
16809            _ => return None,
16810        }
16811    }
16812}
16813
16814/// The callable declarator of the declaration that covers `start_byte`.
16815///
16816/// A consumer that holds a declaration's recorded byte position rather than its
16817/// syntax node - external header extraction, for instance - uses this to reach
16818/// the same `function_declarator` the declaration walk read.
16819pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16820    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16821    loop {
16822        if matches!(
16823            current.kind(),
16824            "declaration" | "field_declaration" | "function_definition"
16825        ) && let Some(declarator) = current
16826            .child_by_field_name("declarator")
16827            .and_then(extract_function_declarator)
16828        {
16829            return Some(declarator);
16830        }
16831        current = current.parent()?;
16832    }
16833}
16834
16835fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16836    let mut labels = Vec::new();
16837    let mut cursor = parameters_node.walk();
16838    for child in parameters_node.children(&mut cursor) {
16839        match child.kind() {
16840            "parameter_declaration" | "optional_parameter_declaration" => {
16841                if let Some(name_node) = child
16842                    .child_by_field_name("declarator")
16843                    .and_then(cpp_declarator_label_node)
16844                {
16845                    labels.push(name_node);
16846                } else {
16847                    labels.push(child);
16848                }
16849            }
16850            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16851                labels.push(child);
16852            }
16853            _ => {}
16854        }
16855    }
16856    labels
16857}
16858
16859fn cpp_signature_search_start<'tree>(
16860    signature: &str,
16861    function_declarator: Node<'tree>,
16862    source: &str,
16863    ancestry: &ParentIndex<'tree>,
16864) -> usize {
16865    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16866        return 0;
16867    };
16868    let raw = node_text(enclosing, source);
16869    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16870    let offset = function_declarator
16871        .start_byte()
16872        .saturating_sub(enclosing.start_byte())
16873        .saturating_sub(leading_trim_bytes);
16874    offset.min(signature.len())
16875}
16876
16877fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16878    match node.kind() {
16879        "identifier" | "field_identifier" => Some(node),
16880        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16881            .child_by_field_name("declarator")
16882            .or_else(|| last_named_child(node))
16883            .and_then(cpp_declarator_label_node),
16884        "array_declarator" => node
16885            .child_by_field_name("declarator")
16886            .and_then(cpp_declarator_label_node),
16887        "function_declarator" => node
16888            .child_by_field_name("declarator")
16889            .or_else(|| node.child_by_field_name("name"))
16890            .or_else(|| last_named_child(node))
16891            .and_then(cpp_declarator_label_node),
16892        _ => None,
16893    }
16894}
16895
16896fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16897    let base_type = parameter
16898        .child_by_field_name("type")
16899        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16900        .unwrap_or_default();
16901    let declarator = cpp_parameter_declarator(parameter);
16902    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
16903    // are discarded, so `f(const int)` and `f(int)` declare one function. A
16904    // qualifier written next to the parameter's type is only top-level when
16905    // the declarator adds no indirection; behind a pointer, reference or array
16906    // declarator the same qualifier belongs to the pointee, referent or
16907    // element and keeps distinguishing the type (#1827).
16908    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16909    let mut cursor = parameter.walk();
16910    let qualifiers = parameter
16911        .named_children(&mut cursor)
16912        .filter(|child| child.kind() == "type_qualifier")
16913        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16914        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16915        .collect::<Vec<_>>()
16916        .join(" ");
16917    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16918        (true, _) => base_type,
16919        (_, true) => qualifiers,
16920        (false, false) => format!("{qualifiers} {base_type}"),
16921    };
16922    let declarator_suffix = declarator
16923        .map(|node| cpp_declarator_suffix_without_name(node, source))
16924        .unwrap_or_default();
16925
16926    let combined = if type_text.is_empty() {
16927        declarator_suffix
16928    } else if declarator_suffix.is_empty() {
16929        type_text
16930    } else {
16931        format!("{type_text} {declarator_suffix}")
16932    };
16933    normalize_cpp_type_text(&combined)
16934}
16935
16936fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16937    parameter.child_by_field_name("declarator").or_else(|| {
16938        // Some unnamed prototype parameters expose their abstract declarator
16939        // as a direct named child without the grammar's `declarator` field.
16940        // Recover only the structured abstract-declarator node; the parameter's
16941        // type and qualifiers are distinct children and must not be guessed from
16942        // source text.
16943        cpp_first_abstract_declarator_child(parameter)
16944    })
16945}
16946
16947/// Whether a parameter's declarator chain adds indirection - a pointer,
16948/// reference, array or function declarator - to the parameter's written type.
16949pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16950    let mut current = Some(declarator);
16951    while let Some(node) = current {
16952        if cpp_is_member_pointer_declarator(node) {
16953            return true;
16954        }
16955        if matches!(
16956            node.kind(),
16957            "pointer_declarator"
16958                | "abstract_pointer_declarator"
16959                | "reference_declarator"
16960                | "abstract_reference_declarator"
16961                | "array_declarator"
16962                | "abstract_array_declarator"
16963                | "function_declarator"
16964                | "abstract_function_declarator"
16965        ) {
16966            return true;
16967        }
16968        current = cpp_nested_declarator(node);
16969    }
16970    false
16971}
16972
16973fn is_cpp_abstract_declarator(kind: &str) -> bool {
16974    matches!(
16975        kind,
16976        "abstract_pointer_declarator"
16977            | "abstract_reference_declarator"
16978            | "abstract_array_declarator"
16979            | "abstract_function_declarator"
16980            | "abstract_parenthesized_declarator"
16981    )
16982}
16983
16984fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16985    let mut cursor = node.walk();
16986    node.named_children(&mut cursor)
16987        .find(|child| is_cpp_abstract_declarator(child.kind()))
16988}
16989
16990fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16991    node.child_by_field_name("declarator").or_else(|| {
16992        if is_cpp_abstract_declarator(node.kind()) {
16993            cpp_first_abstract_declarator_child(node)
16994        } else {
16995            // Named declarators historically use their last named child when
16996            // tree-sitter omits the field. Keep that broad fallback for
16997            // attributed, variadic, and recovered named shapes.
16998            last_named_child(node)
16999        }
17000    })
17001}
17002
17003pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
17004    matches!(
17005        node.kind(),
17006        "reference_declarator" | "abstract_reference_declarator"
17007    ) && node
17008        .children(&mut node.walk())
17009        .any(|child| child.kind() == "&&")
17010}
17011
17012/// Whether `node` is a pointer-to-member declarator, `Left::*cb` or `Left::*`.
17013/// brokk-tree-sitter-cpp 0.24.7 reads one as a `qualified_identifier` whose
17014/// `name` is a pointer declarator, possibly behind further scopes
17015/// (`A::B::*p`), with the owning class in `scope`.
17016fn cpp_is_member_pointer_declarator(node: Node<'_>) -> bool {
17017    let mut current = node;
17018    while current.kind() == "qualified_identifier" {
17019        let Some(name) = current.child_by_field_name("name") else {
17020            return false;
17021        };
17022        if matches!(
17023            name.kind(),
17024            "pointer_declarator" | "abstract_pointer_declarator" | "pointer_type_declarator"
17025        ) {
17026            return true;
17027        }
17028        current = name;
17029    }
17030    false
17031}
17032
17033fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
17034    match node.kind() {
17035        "identifier" | "field_identifier" => String::new(),
17036        // `Left::*cb` keeps its owning class, so `void (Left::*)()` and
17037        // `void (Right::*)()` stay distinct overloads (#6902 item 6).
17038        "qualified_identifier" if cpp_is_member_pointer_declarator(node) => {
17039            let scope = node
17040                .child_by_field_name("scope")
17041                .map(|scope| normalize_cpp_whitespace(node_text(scope, source)))
17042                .unwrap_or_default();
17043            let name = node
17044                .child_by_field_name("name")
17045                .map(|name| cpp_declarator_suffix_without_name(name, source))
17046                .unwrap_or_default();
17047            format!("{scope}::{name}")
17048        }
17049        "pointer_declarator" | "abstract_pointer_declarator" => {
17050            let inner = cpp_nested_declarator(node)
17051                .map(|child| cpp_declarator_suffix_without_name(child, source))
17052                .unwrap_or_default();
17053            format!("*{inner}")
17054        }
17055        "reference_declarator" | "abstract_reference_declarator" => {
17056            let inner = cpp_nested_declarator(node)
17057                .map(|child| cpp_declarator_suffix_without_name(child, source))
17058                .unwrap_or_default();
17059            let reference = if reference_declarator_is_rvalue(node) {
17060                "&&"
17061            } else {
17062                "&"
17063            };
17064            format!("{reference}{inner}")
17065        }
17066        "array_declarator" | "abstract_array_declarator" => {
17067            let inner = cpp_nested_declarator(node)
17068                .map(|child| cpp_declarator_suffix_without_name(child, source))
17069                .unwrap_or_default();
17070            let size = node
17071                .child_by_field_name("size")
17072                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17073                .unwrap_or_default();
17074            format!("{inner}[{size}]")
17075        }
17076        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
17077            let inner = cpp_nested_declarator(node);
17078            inner
17079                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
17080                .unwrap_or_default()
17081        }
17082        "function_declarator" | "abstract_function_declarator" => {
17083            let inner = cpp_nested_declarator(node)
17084                .map(|child| cpp_declarator_suffix_without_name(child, source))
17085                .unwrap_or_default();
17086            let params = node
17087                .child_by_field_name("parameters")
17088                .map(|child| cpp_parameter_signature(child, source))
17089                .unwrap_or_else(|| "()".to_string());
17090            format!("{inner}{params}")
17091        }
17092        _ => {
17093            let text = normalize_cpp_whitespace(node_text(node, source));
17094            let name = extract_declarator_name(node, source);
17095            if name.is_empty() {
17096                text
17097            } else {
17098                text.replace(&name, "").trim().to_string()
17099            }
17100        }
17101    }
17102}
17103
17104fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
17105    collapse_cpp_whitespace(
17106        suffix
17107            .trim()
17108            .trim_start_matches("->")
17109            .trim_start_matches('{')
17110            .trim_end_matches(';'),
17111    )
17112}
17113
17114pub fn normalize_cpp_whitespace(value: &str) -> String {
17115    collapse_cpp_whitespace(value)
17116}
17117
17118fn normalize_cpp_type_text(value: &str) -> String {
17119    collapse_cpp_whitespace(value)
17120        .replace(", ", ",")
17121        .replace(" <", "<")
17122        .replace("< ", "<")
17123        .replace(" >", ">")
17124}
17125
17126fn collapse_cpp_whitespace(value: &str) -> String {
17127    let mut result = String::new();
17128    let mut prev_space = false;
17129    for ch in value.chars() {
17130        if ch.is_whitespace() {
17131            if !prev_space {
17132                result.push(' ');
17133            }
17134            prev_space = true;
17135        } else {
17136            result.push(ch);
17137            prev_space = false;
17138        }
17139    }
17140    result.trim().to_string()
17141}
17142
17143fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
17144    child_by_field_or_named_child_of_kinds(
17145        node,
17146        "body",
17147        &[
17148            "declaration_list",
17149            "field_declaration_list",
17150            "enumerator_list",
17151        ],
17152    )
17153}
17154
17155/// Return a class body's actual closing brace when the parser supplied one.
17156///
17157/// A malformed namespace sentinel can leave a class node carrying unrelated
17158/// parser errors even though its own class body is complete.  `has_error()` is
17159/// therefore too coarse an admission predicate for sentinel ownership.  The
17160/// body list, however, exposes the opening and closing punctuation directly;
17161/// a real (non-missing) final `}` proves that the class did not borrow the
17162/// enclosing namespace's close.  Requiring the body to end before its parent
17163/// container also rejects a recovered node whose body swallowed that outer
17164/// boundary.
17165fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17166    if !matches!(
17167        node.kind(),
17168        "class_specifier" | "struct_specifier" | "union_specifier"
17169    ) {
17170        return None;
17171    }
17172    let body = cpp_body_node(node)?;
17173    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17174        return None;
17175    }
17176    let open = body.child(0)?;
17177    let close = body.child(body.child_count().checked_sub(1)?)?;
17178    if open.kind() != "{"
17179        || open.is_missing()
17180        || close.kind() != "}"
17181        || close.is_missing()
17182        || close.end_byte() != body.end_byte()
17183        || body.end_byte() > node.end_byte()
17184        || node
17185            .parent()
17186            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17187    {
17188        return None;
17189    }
17190    Some(close)
17191}
17192
17193fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17194    let mut stack = vec![node];
17195    while let Some(node) = stack.pop() {
17196        if node.kind() == "namespace_definition" {
17197            return true;
17198        }
17199        push_named_children_reversed(node, &mut stack);
17200    }
17201    false
17202}
17203
17204struct CppNestedNamespaceSentinel<'tree> {
17205    function: Node<'tree>,
17206    body: Node<'tree>,
17207    namespace_components: Vec<String>,
17208}
17209
17210/// Owned structural recovery metadata for a namespace-sentinel region.
17211///
17212/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
17213/// instead of the namespace/class scopes that the declaration visitor restores.
17214/// The inverted usage walk has the original CST, so it needs the same ownership
17215/// evidence without borrowing parser nodes across its file scan.  Keep this
17216/// descriptor deliberately source-range based: callers can match a reference
17217/// node by containment and then resolve its structured type spelling in the
17218/// recovered class scope.
17219#[derive(Debug, Clone)]
17220pub struct CppSentinelRecoveredOwner {
17221    pub range: Range,
17222    /// Start of the qualified owner name (`btree<P>::method`).  A leading
17223    /// return type before this byte is looked up from the namespace; parameters,
17224    /// trailing returns, and the body use the member owner scope.
17225    pub owner_name_start_byte: usize,
17226    /// Number of leading components belonging to the namespace rather than
17227    /// the qualified class owner.  A leading return type is looked up before
17228    /// every owner component, not merely before the innermost class.
17229    pub namespace_component_count: usize,
17230    pub scope_components: Vec<String>,
17231}
17232
17233#[derive(Debug, Clone)]
17234pub struct CppSentinelRecoveredClass {
17235    pub namespace_range: Range,
17236    pub namespace_scope_components: Vec<String>,
17237    pub class_range: Range,
17238    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
17239    pub scope_components: Vec<String>,
17240    /// Qualified out-of-line member definitions owned by this class.  Their
17241    /// ranges may extend beyond `class_range` when the malformed sentinel
17242    /// swallowed the namespace close and left definitions as function siblings.
17243    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17244}
17245
17246/// Resolve the lexical scope restored for a node in a malformed
17247/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
17248/// outrank class spans, which in turn outrank the surviving namespace body.
17249/// The class ancestor suffix is recovered from the original CST so nested
17250/// members keep their complete `Outer::Inner` owner chain.
17251pub fn cpp_sentinel_recovered_scope_for_node(
17252    node: Node<'_>,
17253    source: &str,
17254    recovered_classes: &[CppSentinelRecoveredClass],
17255) -> Option<Vec<String>> {
17256    let contains =
17257        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17258    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17259    for recovered in recovered_classes {
17260        for owner in recovered
17261            .owner_ranges
17262            .iter()
17263            .filter(|owner| contains(owner.range))
17264        {
17265            let replace = best_owner.is_none_or(|existing| {
17266                owner.range.end_byte.saturating_sub(owner.range.start_byte)
17267                    < existing
17268                        .range
17269                        .end_byte
17270                        .saturating_sub(existing.range.start_byte)
17271            });
17272            if replace {
17273                best_owner = Some(owner);
17274            }
17275        }
17276    }
17277    if let Some(owner) = best_owner {
17278        let mut scope = owner.scope_components.clone();
17279        if node.start_byte() < owner.owner_name_start_byte {
17280            scope.truncate(owner.namespace_component_count);
17281        }
17282        return Some(scope);
17283    }
17284
17285    let class = recovered_classes
17286        .iter()
17287        .filter(|recovered| contains(recovered.class_range))
17288        .min_by_key(|recovered| {
17289            recovered
17290                .class_range
17291                .end_byte
17292                .saturating_sub(recovered.class_range.start_byte)
17293        });
17294    let class_scope = class.is_some();
17295    let mut scope = if let Some(class) = class {
17296        class.scope_components.clone()
17297    } else {
17298        let namespace = recovered_classes
17299            .iter()
17300            .filter(|recovered| contains(recovered.namespace_range))
17301            .min_by_key(|recovered| {
17302                recovered
17303                    .namespace_range
17304                    .end_byte
17305                    .saturating_sub(recovered.namespace_range.start_byte)
17306            })?;
17307        let mut scope = namespace.namespace_scope_components.clone();
17308        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17309        let common_prefix = scope
17310            .iter()
17311            .zip(&parser_namespace)
17312            .take_while(|(recovered, parser)| recovered == parser)
17313            .count();
17314        scope.extend(parser_namespace.into_iter().skip(common_prefix));
17315        scope
17316    };
17317    if class_scope {
17318        let mut ancestor_components = Vec::new();
17319        let mut ancestor = node.parent();
17320        while let Some(current) = ancestor {
17321            if matches!(
17322                current.kind(),
17323                "class_specifier" | "struct_specifier" | "union_specifier"
17324            ) && let Some(name) = current.child_by_field_name("name")
17325                && let Some(name_components) = cpp_name_components(name, source)
17326            {
17327                ancestor_components.push(
17328                    name_components
17329                        .into_iter()
17330                        .map(|component| component.name)
17331                        .collect::<Vec<_>>(),
17332                );
17333            }
17334            ancestor = current.parent();
17335        }
17336        ancestor_components.reverse();
17337        let base_len = scope.len();
17338        for component in ancestor_components.into_iter().flatten() {
17339            if scope.len() >= base_len && scope.last() == Some(&component) {
17340                continue;
17341            }
17342            scope.push(component);
17343        }
17344    }
17345    Some(scope)
17346}
17347
17348struct CppSentinelFragmentedClassTail<'tree> {
17349    class_node: Node<'tree>,
17350    template_node: Option<Node<'tree>>,
17351    name: String,
17352    raw_supertypes: Option<Vec<String>>,
17353    fragmented: FragmentedExportBody,
17354    consumed_start: usize,
17355    /// The original node whose direct children after the class open are the
17356    /// class's members: the body of a class node, or the class-prefix `ERROR`
17357    /// itself when the class keyword, name, and `{` are its direct tokens.
17358    member_container: Node<'tree>,
17359}
17360
17361struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17362    name: String,
17363    open: Node<'tree>,
17364    raw_supertypes: Option<Vec<String>>,
17365}
17366
17367struct CppSentinelDirectBodyClassRegion {
17368    namespace_components: Vec<String>,
17369    class_start: usize,
17370    class_start_line: usize,
17371    class_close_end: usize,
17372    class_close_line: usize,
17373    name: String,
17374}
17375
17376fn cpp_sentinel_body_class_candidate<'tree>(
17377    child: Node<'tree>,
17378) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17379    if matches!(
17380        child.kind(),
17381        "class_specifier" | "struct_specifier" | "union_specifier"
17382    ) {
17383        return Some((child, None));
17384    }
17385    if child.kind() != "template_declaration" {
17386        if child.kind() == "declaration" {
17387            return Some((first_class_like_child(child)?, None));
17388        }
17389        return None;
17390    }
17391    let mut cursor = child.walk();
17392    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17393        if matches!(
17394            candidate.kind(),
17395            "class_specifier" | "struct_specifier" | "union_specifier"
17396        ) {
17397            Some(candidate)
17398        } else if candidate.kind() == "declaration" {
17399            first_class_like_child(candidate)
17400        } else {
17401            None
17402        }
17403    })?;
17404    Some((class_node, Some(child)))
17405}
17406
17407/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
17408/// namespace-sentinel body when a later member macro ends the bogus sentinel
17409/// function before the real class close. The anonymous class/open tokens and
17410/// direct identifier are the structural proof; a retained direct close would
17411/// be an ordinary malformed class rather than the fragmented tail handled here.
17412fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17413    node: Node<'tree>,
17414    source: &str,
17415) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17416    let name = malformed_class_error_owner_name(node, source)?;
17417    let mut cursor = node.walk();
17418    let children = node.children(&mut cursor).collect::<Vec<_>>();
17419    let keyword = children.first()?;
17420    let open_index = children.iter().position(|child| child.kind() == "{")?;
17421    if children[open_index + 1..]
17422        .iter()
17423        .any(|child| child.kind() == "}")
17424    {
17425        return None;
17426    }
17427    let raw_supertypes =
17428        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17429    Some(CppSentinelFragmentedClassErrorPrefix {
17430        name,
17431        open: children[open_index],
17432        raw_supertypes,
17433    })
17434}
17435
17436fn cpp_sentinel_direct_body_class_candidate<'tree>(
17437    child: Node<'tree>,
17438) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17439    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17440        return Some(candidate);
17441    }
17442    if child.kind() != "template_declaration" {
17443        return None;
17444    }
17445    let mut cursor = child.walk();
17446    let wrapper = child
17447        .named_children(&mut cursor)
17448        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17449    Some((first_class_like_child(wrapper)?, Some(child)))
17450}
17451
17452fn cpp_sentinel_direct_namespace_components(
17453    function: Node<'_>,
17454    body: Node<'_>,
17455    source: &str,
17456) -> Option<Vec<String>> {
17457    let mut cursor = function.walk();
17458    let children = function
17459        .named_children(&mut cursor)
17460        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17461        .collect::<Vec<_>>();
17462    let sentinel_index = children.iter().rposition(|child| {
17463        direct_identifier_name(*child, source)
17464            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17465    })?;
17466    let mut identifiers = Vec::new();
17467    let mut stack = children[sentinel_index + 1..]
17468        .iter()
17469        .rev()
17470        .copied()
17471        .collect::<Vec<_>>();
17472    while let Some(current) = stack.pop() {
17473        if let Some(name) = direct_identifier_name(current, source) {
17474            identifiers.push(name);
17475            continue;
17476        }
17477        let mut cursor = current.walk();
17478        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17479        stack.extend(children.into_iter().rev());
17480    }
17481    let [keyword, namespace] = identifiers.as_slice() else {
17482        return None;
17483    };
17484    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17485        .then(|| vec![namespace.clone()])
17486}
17487
17488fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17489    let mut sibling = class_semicolon.next_named_sibling();
17490    let namespace_close = loop {
17491        let Some(current) = sibling else {
17492            return false;
17493        };
17494        sibling = current.next_named_sibling();
17495        if current.kind() != "comment" {
17496            break current;
17497        }
17498    };
17499    if !cpp_is_stray_close_brace(namespace_close, source) {
17500        return false;
17501    }
17502    loop {
17503        let Some(current) = sibling else {
17504            return false;
17505        };
17506        sibling = current.next_named_sibling();
17507        if current.kind() == "comment" {
17508            continue;
17509        }
17510        return direct_identifier_name(current, source)
17511            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17512    }
17513}
17514
17515fn cpp_sentinel_macro_body_class_region<'tree>(
17516    node: Node<'tree>,
17517    source: &str,
17518    ancestry: &ParentIndex<'tree>,
17519) -> Option<CppSentinelDirectBodyClassRegion> {
17520    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17521        return None;
17522    };
17523    if node.kind() != "function_definition" || !node.has_error() {
17524        return None;
17525    }
17526    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17527    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17528    let mut cursor = body.walk();
17529    let candidates = body
17530        .named_children(&mut cursor)
17531        .filter_map(cpp_sentinel_direct_body_class_candidate)
17532        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17533        .collect::<Vec<_>>();
17534    let [(class_node, template_node)] = candidates.as_slice() else {
17535        return None;
17536    };
17537    let original_body = cpp_body_node(*class_node)?;
17538    let name = class_like_name(*class_node, source, ancestry)?;
17539    if name.is_empty() {
17540        return None;
17541    }
17542
17543    let mut sibling = node.next_named_sibling();
17544    let (class_close_start, class_close_end, class_close_line) = loop {
17545        let current = sibling?;
17546        let next = current.next_named_sibling();
17547        if cpp_is_stray_close_brace(current, source)
17548            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17549        {
17550            let semicolon = next.expect("checked above");
17551            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17552                return None;
17553            }
17554            break (
17555                current.start_byte(),
17556                semicolon.end_byte(),
17557                semicolon.end_position().row + 1,
17558            );
17559        }
17560        sibling = next;
17561    };
17562    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17563    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17564    let root = tree.root_node();
17565    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17566    // The region reparse is its own tree, so it needs its own parent index;
17567    // the caller's index answers nothing about these nodes.
17568    let reparsed_ancestry = ParentIndex::new(root);
17569    let reparsed =
17570        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17571    if reparsed.name != name
17572        || reparsed.declaration_node.start_byte() != class_node.start_byte()
17573        || reparsed.body.start_byte() != original_body.start_byte()
17574        || class_close_start <= reparsed.body.end_byte()
17575        || class_close_end <= class_node.end_byte()
17576    {
17577        return None;
17578    }
17579    Some(CppSentinelDirectBodyClassRegion {
17580        namespace_components,
17581        class_start: reparse_start,
17582        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17583            node.start_position().row + 1
17584        }),
17585        class_close_end,
17586        class_close_line,
17587        name,
17588    })
17589}
17590
17591/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
17592/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
17593///
17594/// The parser puts the namespace opener and the malformed function in one root
17595/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
17596/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
17597/// the malformed function must begin with an all-caps type, then an ERROR whose
17598/// sole identifier is `namespace`, followed by the inner namespace identifier
17599/// and a compound body; and that body must contain a complete named class or a
17600/// structurally fragmented class prefix. A text reparse cannot prove any of
17601/// those ownership boundaries.
17602fn cpp_nested_namespace_sentinel<'tree>(
17603    node: Node<'tree>,
17604    source: &str,
17605    ancestry: &ParentIndex<'tree>,
17606) -> Option<CppNestedNamespaceSentinel<'tree>> {
17607    // The shape checks below are the admission proof: a `type_identifier`
17608    // sentinel followed by an `ERROR` holding the `namespace` keyword implies
17609    // parser damage on its own, and under the macro-declaration grammar the
17610    // `macro_modifier` wrapper and the keyword in the type field are each
17611    // admitted only where the parse would otherwise contain an error.
17612    let (function, mut namespace_components) = if node.kind() == "ERROR" {
17613        let mut cursor = node.walk();
17614        let functions = node
17615            .named_children(&mut cursor)
17616            .filter(|child| child.kind() == "function_definition")
17617            .collect::<Vec<_>>();
17618        let [function] = functions.as_slice() else {
17619            return None;
17620        };
17621        let mut cursor = node.walk();
17622        let children = node.children(&mut cursor).collect::<Vec<_>>();
17623        let function_index = children
17624            .iter()
17625            .position(|child| same_node(*child, *function))?;
17626        let [outer_keyword, outer_name, outer_open] =
17627            children.get(function_index.checked_sub(3)?..function_index)?
17628        else {
17629            return None;
17630        };
17631        if outer_keyword.kind() != "namespace"
17632            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17633            || outer_open.kind() != "{"
17634        {
17635            return None;
17636        }
17637        (
17638            *function,
17639            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17640        )
17641    } else if node.kind() == "function_definition" {
17642        let declaration_list = node.parent()?;
17643        let namespace = declaration_list.parent()?;
17644        if declaration_list.kind() != "declaration_list"
17645            || namespace.kind() != "namespace_definition"
17646            || namespace.child_by_field_name("body") != Some(declaration_list)
17647        {
17648            return None;
17649        }
17650        (node, Vec::new())
17651    } else {
17652        return None;
17653    };
17654
17655    let mut cursor = function.walk();
17656    let named = function
17657        .named_children(&mut cursor)
17658        .filter(|child| child.kind() != "comment")
17659        .collect::<Vec<_>>();
17660    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17661        return None;
17662    };
17663    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
17664    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
17665    // the same token is wrapped in.
17666    let sentinel = if first_type.kind() == "type_identifier" {
17667        normalize_cpp_whitespace(node_text(*first_type, source))
17668    } else {
17669        let identifier = macro_modifier_identifier(*first_type)?;
17670        normalize_cpp_whitespace(node_text(identifier, source))
17671    };
17672    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17673        return None;
17674    }
17675    // The mis-lexed `namespace` keyword: an `ERROR` holding it under the older
17676    // grammar, or the function's bogus type field under the macro-declaration
17677    // grammar, where a keyword cannot be a type.
17678    let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17679        inner_error.named_child(0)?
17680    } else if inner_error.kind() == "type_identifier" {
17681        *inner_error
17682    } else {
17683        return None;
17684    };
17685    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17686        return None;
17687    }
17688    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17689        return None;
17690    }
17691    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17692    if inner_name.is_empty() || body.kind() != "compound_statement" {
17693        return None;
17694    }
17695    namespace_components.push(inner_name);
17696
17697    let mut cursor = body.walk();
17698    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17699        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17700            cpp_body_node(class_node).is_some()
17701                && class_like_name(class_node, source, ancestry)
17702                    .is_some_and(|name| !name.is_empty())
17703        })
17704    });
17705    if !has_complete_class
17706        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17707    {
17708        return None;
17709    }
17710
17711    Some(CppNestedNamespaceSentinel {
17712        function,
17713        body: *body,
17714        namespace_components,
17715    })
17716}
17717
17718/// Recognize a namespace-begin sentinel directly beneath the translation unit.
17719///
17720/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
17721/// function whose type is the sentinel, whose declarator is the structured
17722/// qualified name `namespace::a::b`, and whose body contains the namespace
17723/// items. Declaration indexing already reparses this bounded region. The
17724/// inverse scanner retains the original tree, so recover the same namespace
17725/// components from the declarator fields for its lexical-scope metadata.
17726fn cpp_root_namespace_sentinel<'tree>(
17727    node: Node<'tree>,
17728    source: &str,
17729    ancestry: &ParentIndex<'tree>,
17730) -> Option<CppNestedNamespaceSentinel<'tree>> {
17731    if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17732        return None;
17733    }
17734    let body = node.child_by_field_name("body")?;
17735    let declarator = node.child_by_field_name("declarator")?;
17736    let mut cursor = node.walk();
17737    let named = node
17738        .named_children(&mut cursor)
17739        .filter(|child| child.kind() != "comment")
17740        .collect::<Vec<_>>();
17741    if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17742        return None;
17743    }
17744    // The sentinel is the bogus return `type` (`type_identifier`), with the
17745    // literal `namespace` keyword leading the qualified declarator -- or,
17746    // under the macro-declaration grammar, a `macro_modifier`, with the
17747    // keyword occupying the type field (a keyword cannot be a type, which is
17748    // the same recovery evidence) and the namespace path as the declarator.
17749    let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17750        [first, second, _] => {
17751            let first_type = node.child_by_field_name("type")?;
17752            if !same_node(*first, first_type)
17753                || first_type.kind() != "type_identifier"
17754                || !same_node(*second, declarator)
17755                || !node.has_error()
17756            {
17757                return None;
17758            }
17759            (
17760                normalize_cpp_whitespace(node_text(first_type, source)),
17761                true,
17762            )
17763        }
17764        [first, second, third, _] => {
17765            let type_node = node.child_by_field_name("type")?;
17766            let identifier = macro_modifier_identifier(*first)?;
17767            if !same_node(*second, type_node)
17768                || type_node.kind() != "type_identifier"
17769                || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17770                || !same_node(*third, declarator)
17771            {
17772                return None;
17773            }
17774            (
17775                normalize_cpp_whitespace(node_text(identifier, source)),
17776                false,
17777            )
17778        }
17779        _ => return None,
17780    };
17781    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17782        return None;
17783    }
17784    if !matches!(
17785        declarator.kind(),
17786        "qualified_identifier" | "identifier" | "namespace_identifier"
17787    ) {
17788        return None;
17789    }
17790    let mut declarator_components = Vec::new();
17791    let mut valid_components = true;
17792    walk_named_tree_preorder(declarator, true, |component| {
17793        if !matches!(
17794            component.kind(),
17795            "identifier" | "namespace_identifier" | "type_identifier"
17796        ) {
17797            return WalkControl::Continue;
17798        }
17799        let Some(component) = canonical_cpp_qualified_component(component, source) else {
17800            valid_components = false;
17801            return WalkControl::Break;
17802        };
17803        declarator_components.push(component.name);
17804        WalkControl::SkipChildren
17805    });
17806    if strip_namespace_keyword {
17807        if !valid_components
17808            || declarator_components.first().map(String::as_str) != Some("namespace")
17809        {
17810            return None;
17811        }
17812        declarator_components.remove(0);
17813    }
17814    let namespace_components = declarator_components;
17815    if !valid_components
17816        || namespace_components.is_empty()
17817        || namespace_components
17818            .iter()
17819            .any(|component| component.is_empty() || cpp_export_macro_token(component))
17820    {
17821        return None;
17822    }
17823
17824    let mut cursor = body.walk();
17825    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17826        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17827            cpp_body_node(class_node).is_some()
17828                && class_like_name(class_node, source, ancestry)
17829                    .is_some_and(|name| !name.is_empty())
17830        })
17831    });
17832    if !has_complete_class
17833        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17834    {
17835        return None;
17836    }
17837
17838    Some(CppNestedNamespaceSentinel {
17839        function: node,
17840        body,
17841        namespace_components,
17842    })
17843}
17844
17845/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
17846/// malformed namespace-sentinel function.  The recovery is deliberately
17847/// structural: the class must be a direct body item, its own class node must be
17848/// erroneous and end before a unique anonymous `}` in the enclosing
17849/// declaration-list, and that namespace's next sibling must be a standalone
17850/// `;`.  The complete interior must pass the existing member-shaped reparse
17851/// gate. This avoids source brace scans and does not borrow a close from an
17852/// unrelated later declaration.
17853fn cpp_sentinel_fragmented_class_tail<'tree>(
17854    function: Node<'tree>,
17855    body: Node<'tree>,
17856    source: &str,
17857    ancestry: &ParentIndex<'tree>,
17858) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17859    let mut cursor = body.walk();
17860    let candidates = body
17861        .named_children(&mut cursor)
17862        .filter_map(|child| {
17863            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17864                let class_body = cpp_body_node(class_node)?;
17865                if !class_node.has_error() {
17866                    return None;
17867                }
17868                let name = class_like_name(class_node, source, ancestry)?;
17869                let raw_supertypes =
17870                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17871                        .then(|| extract_cpp_supertypes(class_node, source));
17872                return Some((
17873                    class_node,
17874                    template_node,
17875                    name,
17876                    class_body,
17877                    class_body.start_byte().checked_add(1)?,
17878                    raw_supertypes,
17879                    class_body,
17880                ));
17881            }
17882            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17883            Some((
17884                child,
17885                None,
17886                prefix.name,
17887                prefix.open,
17888                prefix.open.end_byte(),
17889                prefix.raw_supertypes,
17890                child,
17891            ))
17892        })
17893        .collect::<Vec<_>>();
17894    let [
17895        (
17896            class_node,
17897            template_node,
17898            name,
17899            class_body,
17900            reparse_start,
17901            raw_supertypes,
17902            member_container,
17903        ),
17904    ] = candidates.as_slice()
17905    else {
17906        return None;
17907    };
17908    if name.is_empty() {
17909        return None;
17910    }
17911
17912    let (close, semicolon) =
17913        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17914
17915    let reparse_end = close.start_byte();
17916    if *reparse_start >= reparse_end {
17917        return None;
17918    }
17919    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17920    if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17921        return None;
17922    }
17923    let class_range = Range {
17924        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17925        end_byte: semicolon.end_byte(),
17926        start_line: template_node.map_or(class_node.start_position().row, |node| {
17927            node.start_position().row
17928        }) + 1,
17929        end_line: semicolon.end_position().row + 1,
17930    };
17931    Some(CppSentinelFragmentedClassTail {
17932        class_node: *class_node,
17933        template_node: *template_node,
17934        name: name.clone(),
17935        raw_supertypes: raw_supertypes.clone(),
17936        fragmented: FragmentedExportBody {
17937            reparse_start: *reparse_start,
17938            reparse_end,
17939            class_range,
17940        },
17941        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17942        member_container: *member_container,
17943    })
17944}
17945
17946/// The alias declarations a fragmented sentinel class still holds as
17947/// structured direct members in the original tree (#6902).
17948///
17949/// The member reparse cannot keep them: after an access label it reads
17950/// `using value_type = int;` in statement context, as a `using` variable
17951/// named `value_type`, and loses the target. The original node keeps the
17952/// alias kind, the target type, the exact source range, and the template and
17953/// preprocessor context. Ownership is proven by position alone: a direct
17954/// child of the class's member container, inside the interior the member
17955/// reparse covers, or inside a preprocessor conditional that is one. An alias
17956/// nested anywhere else is not admitted here.
17957fn cpp_sentinel_fragmented_class_aliases<'tree>(
17958    tail: &CppSentinelFragmentedClassTail<'tree>,
17959) -> Vec<Node<'tree>> {
17960    let mut aliases = Vec::new();
17961    let mut containers = vec![tail.member_container];
17962    while let Some(container) = containers.pop() {
17963        let mut cursor = container.walk();
17964        for child in container.named_children(&mut cursor) {
17965            if child.start_byte() < tail.fragmented.reparse_start
17966                || child.end_byte() > tail.fragmented.reparse_end
17967            {
17968                continue;
17969            }
17970            match child.kind() {
17971                // A conditional member keeps its owner; its guard comes from
17972                // this same original ancestry.
17973                "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
17974                | "preproc_elifdef" => containers.push(child),
17975                "alias_declaration" if !child.has_error() => aliases.push(child),
17976                "template_declaration"
17977                    if !child.has_error()
17978                        && child
17979                            .named_children(&mut child.walk())
17980                            .any(|declaration| declaration.kind() == "alias_declaration") =>
17981                {
17982                    aliases.push(child)
17983                }
17984                _ => {}
17985            }
17986        }
17987    }
17988    aliases
17989}
17990
17991/// Recover the class and out-of-line owner scopes from every malformed
17992/// namespace-sentinel region in `root`.
17993///
17994/// This is the shared structural counterpart to
17995/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
17996/// reuses the visitor's sentinel/class admission predicates instead of parsing
17997/// source text a second time.  The returned values own only ranges and names, so
17998/// they can be retained by an inverted usage scan after the tree borrow ends.
17999pub fn cpp_sentinel_recovered_classes(
18000    root: Node<'_>,
18001    source: &str,
18002) -> Vec<CppSentinelRecoveredClass> {
18003    // The macro-declaration grammar absorbs the sentinel damage into structured
18004    // nodes and can leave the whole tree clean: `macro_modifier` and its kin are
18005    // admitted only where the older grammar would have reported an error, so a
18006    // clean tree carrying them holds the same recovery evidence. A tree with
18007    // neither errors nor macro-declaration nodes has no sentinel region.
18008    if !root.has_error()
18009        && !subtree_contains(root, |node| {
18010            matches!(
18011                node.kind(),
18012                "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
18013            )
18014        })
18015    {
18016        return Vec::new();
18017    }
18018    // This scan owns its walk of `root`, so it owns the parent index that walk
18019    // asks its ancestor questions through. Built after the error gate: a clean
18020    // tree without macro-declaration evidence returns without paying for one.
18021    let ancestry = ParentIndex::new(root);
18022    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
18023    let mut stack = vec![root];
18024    while let Some(current) = stack.pop() {
18025        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
18026            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
18027        {
18028            let namespace_components = cpp_sentinel_recovered_namespace_components(
18029                recovered.function,
18030                &recovered.namespace_components,
18031                source,
18032            );
18033            let fragmented = cpp_sentinel_fragmented_class_tail(
18034                recovered.function,
18035                recovered.body,
18036                source,
18037                &ancestry,
18038            );
18039            let mut class_candidates = Vec::new();
18040            let mut cursor = recovered.body.walk();
18041            for (class_node, template_node) in recovered
18042                .body
18043                .named_children(&mut cursor)
18044                .filter_map(cpp_sentinel_body_class_candidate)
18045            {
18046                let Some(name) = class_like_name(class_node, source, &ancestry) else {
18047                    continue;
18048                };
18049                if name.is_empty() {
18050                    continue;
18051                }
18052                let is_fragmented = fragmented
18053                    .as_ref()
18054                    .is_some_and(|tail| same_node(tail.class_node, class_node));
18055                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
18056                    continue;
18057                }
18058                let class_range = if is_fragmented {
18059                    fragmented
18060                        .as_ref()
18061                        .map(|tail| tail.fragmented.class_range)
18062                        .expect("fragmented class range is present when class matches")
18063                } else {
18064                    cpp_declaration_range(template_node.unwrap_or(class_node))
18065                };
18066                class_candidates.push((class_range, name));
18067            }
18068            if let Some(fragmented) = fragmented
18069                .as_ref()
18070                .filter(|tail| tail.class_node.kind() == "ERROR")
18071            {
18072                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
18073            }
18074
18075            let mut owner_ranges =
18076                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
18077            cpp_sentinel_extend_unique_owner_ranges(
18078                &mut owner_ranges,
18079                cpp_sentinel_recovered_sibling_owner_ranges(
18080                    recovered.function,
18081                    &namespace_components,
18082                    source,
18083                ),
18084            );
18085            for (class_range, name) in class_candidates {
18086                push_cpp_sentinel_recovered_class(
18087                    &mut recovered_classes,
18088                    cpp_declaration_range(recovered.body),
18089                    &namespace_components,
18090                    class_range,
18091                    name,
18092                    &owner_ranges,
18093                );
18094            }
18095
18096            if let Some(declaration_list) = recovered
18097                .function
18098                .parent()
18099                .filter(|parent| parent.kind() == "declaration_list")
18100            {
18101                let outer_namespace =
18102                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
18103                push_cpp_sentinel_sibling_classes(
18104                    &mut recovered_classes,
18105                    declaration_list,
18106                    recovered.function,
18107                    &outer_namespace,
18108                    source,
18109                    &ancestry,
18110                );
18111            }
18112        } else if let Some(region) =
18113            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
18114        {
18115            let namespace_components = cpp_sentinel_recovered_namespace_components(
18116                current,
18117                &region.namespace_components,
18118                source,
18119            );
18120            let owner_container = current
18121                .parent()
18122                .filter(|parent| parent.kind() == "declaration_list")
18123                .unwrap_or(current);
18124            let owner_ranges =
18125                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18126            push_cpp_sentinel_recovered_class(
18127                &mut recovered_classes,
18128                cpp_declaration_range(owner_container),
18129                &namespace_components,
18130                Range {
18131                    start_byte: region.class_start,
18132                    end_byte: region.class_close_end,
18133                    start_line: region.class_start_line,
18134                    end_line: region.class_close_line,
18135                },
18136                region.name,
18137                &owner_ranges,
18138            );
18139        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
18140            // A generic sentinel-prefixed class can be reduced as a malformed
18141            // function/ERROR without the explicit `namespace X` token pair.
18142            // Reuse the declaration visitor's bounded reparse and retain only
18143            // the recovered class identity/range here.
18144            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
18145                region;
18146            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18147                continue;
18148            };
18149            let root = tree.root_node();
18150            let template_node = cpp_sentinel_reparsed_leading_template(root);
18151            // A region reparse is its own tree and needs its own parent index.
18152            let reparsed_ancestry = ParentIndex::new(root);
18153            let Some(reparsed_class) =
18154                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
18155            else {
18156                continue;
18157            };
18158            let class_node = reparsed_class.declaration_node;
18159            let name = reparsed_class.name;
18160            let namespace_components =
18161                cpp_sentinel_recovered_namespace_components(current, &[], source);
18162            let owner_container = current
18163                .parent()
18164                .filter(|parent| parent.kind() == "declaration_list")
18165                .unwrap_or(current);
18166            let mut owner_ranges =
18167                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18168            cpp_sentinel_extend_unique_owner_ranges(
18169                &mut owner_ranges,
18170                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18171            );
18172            push_cpp_sentinel_recovered_class(
18173                &mut recovered_classes,
18174                cpp_declaration_range(owner_container),
18175                &namespace_components,
18176                Range {
18177                    start_byte: class_start,
18178                    end_byte: close_end,
18179                    start_line: class_node.start_position().row + 1,
18180                    end_line: class_node.end_position().row + 1,
18181                },
18182                name,
18183                &owner_ranges,
18184            );
18185            if owner_container.kind() == "declaration_list" {
18186                push_cpp_sentinel_sibling_classes(
18187                    &mut recovered_classes,
18188                    owner_container,
18189                    current,
18190                    &namespace_components,
18191                    source,
18192                    &ancestry,
18193                );
18194            }
18195        }
18196
18197        let mut cursor = current.walk();
18198        stack.extend(current.named_children(&mut cursor));
18199    }
18200    // A shallower sentinel can expose nested classes as apparent namespace
18201    // siblings even after a deeper sentinel proves that a containing class
18202    // owns their ranges. Drop those shadow descriptors; scope recovery starts
18203    // from the proven containing class and appends parser-visible class
18204    // ancestors, preserving the full `Outer::Inner` chain.
18205    let shadowed = recovered_classes
18206        .iter()
18207        .map(|candidate| {
18208            recovered_classes.iter().any(|container| {
18209                container.class_range.start_byte <= candidate.class_range.start_byte
18210                    && container.class_range.end_byte >= candidate.class_range.end_byte
18211                    && container.class_range != candidate.class_range
18212                    && container.namespace_scope_components.len()
18213                        > candidate.namespace_scope_components.len()
18214                    && container
18215                        .namespace_scope_components
18216                        .starts_with(&candidate.namespace_scope_components)
18217            })
18218        })
18219        .collect::<Vec<_>>();
18220    let mut index = 0usize;
18221    recovered_classes.retain(|_| {
18222        let keep = !shadowed[index];
18223        index += 1;
18224        keep
18225    });
18226    recovered_classes
18227}
18228
18229/// A flat sentinel can swallow the first class while leaving later classes and
18230/// their out-of-line definitions as ordinary declaration-list siblings.  Once
18231/// the malformed class proves the sentinel envelope, retain those structurally
18232/// complete sibling classes under the same surviving namespace so every member
18233/// owner in the region uses one recovery contract.
18234fn push_cpp_sentinel_sibling_classes<'tree>(
18235    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18236    declaration_list: Node<'tree>,
18237    sentinel_node: Node<'tree>,
18238    namespace_components: &[String],
18239    source: &str,
18240    ancestry: &ParentIndex<'tree>,
18241) {
18242    let owner_ranges =
18243        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18244    let namespace_range = cpp_declaration_range(declaration_list);
18245    let mut cursor = declaration_list.walk();
18246    for (class_node, template_node) in declaration_list
18247        .named_children(&mut cursor)
18248        .filter(|child| !same_node(*child, sentinel_node))
18249        .filter_map(cpp_sentinel_body_class_candidate)
18250    {
18251        let Some(name) = class_like_name(class_node, source, ancestry) else {
18252            continue;
18253        };
18254        if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18255            continue;
18256        }
18257        push_cpp_sentinel_recovered_class(
18258            recovered_classes,
18259            namespace_range,
18260            namespace_components,
18261            cpp_declaration_range(template_node.unwrap_or(class_node)),
18262            name,
18263            &owner_ranges,
18264        );
18265    }
18266}
18267
18268fn push_cpp_sentinel_recovered_class(
18269    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18270    namespace_range: Range,
18271    namespace_components: &[String],
18272    class_range: Range,
18273    name: String,
18274    owner_ranges: &[CppSentinelRecoveredOwner],
18275) {
18276    let mut scope_components = namespace_components.to_vec();
18277    scope_components.push(name);
18278    let owner_ranges = owner_ranges
18279        .iter()
18280        .filter(|owner| owner.scope_components.starts_with(&scope_components))
18281        .cloned()
18282        .collect::<Vec<_>>();
18283    if recovered_classes.iter().any(|existing| {
18284        existing.class_range == class_range && existing.scope_components == scope_components
18285    }) {
18286        return;
18287    }
18288    recovered_classes.push(CppSentinelRecoveredClass {
18289        namespace_range,
18290        namespace_scope_components: namespace_components.to_vec(),
18291        class_range,
18292        scope_components,
18293        owner_ranges,
18294    });
18295}
18296
18297fn cpp_sentinel_recovered_namespace_components(
18298    function: Node<'_>,
18299    recovered_components: &[String],
18300    source: &str,
18301) -> Vec<String> {
18302    let mut ancestor_components = Vec::new();
18303    let mut ancestor = function.parent();
18304    while let Some(current) = ancestor {
18305        if current.kind() == "namespace_definition"
18306            && let Some(name_node) = current.child_by_field_name("name")
18307            && let Some(components) = cpp_name_components(name_node, source)
18308        {
18309            ancestor_components.push(
18310                components
18311                    .into_iter()
18312                    .map(|component| component.name)
18313                    .collect::<Vec<_>>(),
18314            );
18315        }
18316        ancestor = current.parent();
18317    }
18318    ancestor_components.reverse();
18319    let mut ancestors = ancestor_components
18320        .into_iter()
18321        .flatten()
18322        .collect::<Vec<_>>();
18323
18324    let overlap = (0..=ancestors.len().min(recovered_components.len()))
18325        .rev()
18326        .find(|length| {
18327            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18328        })
18329        .unwrap_or(0);
18330    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18331    ancestors
18332}
18333
18334fn cpp_sentinel_recovered_owner_ranges(
18335    body: Node<'_>,
18336    namespace_components: &[String],
18337    source: &str,
18338) -> Vec<CppSentinelRecoveredOwner> {
18339    let mut owners = Vec::new();
18340    walk_named_tree_preorder(body, true, |node| {
18341        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18342    });
18343    owners
18344}
18345
18346fn cpp_sentinel_collect_owner_range(
18347    node: Node<'_>,
18348    namespace_components: &[String],
18349    source: &str,
18350    owners: &mut Vec<CppSentinelRecoveredOwner>,
18351) -> WalkControl {
18352    if node.kind() != "function_definition" {
18353        return WalkControl::Continue;
18354    }
18355    let Some(function_declarator) = extract_function_declarator(node) else {
18356        return WalkControl::Continue;
18357    };
18358    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18359        return WalkControl::Continue;
18360    };
18361    let Some(mut components) = cpp_name_components(name_node, source) else {
18362        return WalkControl::Continue;
18363    };
18364    if components.len() <= 1 {
18365        return WalkControl::Continue;
18366    }
18367    components.pop();
18368    let mut owner_components = components
18369        .into_iter()
18370        .map(|component| component.name)
18371        .collect::<Vec<_>>();
18372    let overlap = (0..=namespace_components.len().min(owner_components.len()))
18373        .rev()
18374        .find(|length| {
18375            owner_components[..*length]
18376                == namespace_components[namespace_components.len().saturating_sub(*length)..]
18377        })
18378        .unwrap_or(0);
18379    let mut scope_components = namespace_components.to_vec();
18380    scope_components.extend(owner_components.drain(overlap..));
18381    if scope_components.len() <= namespace_components.len() {
18382        return WalkControl::Continue;
18383    }
18384    let range = cpp_declaration_range(node);
18385    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18386        existing.range == range && existing.scope_components == scope_components
18387    }) {
18388        owners.push(CppSentinelRecoveredOwner {
18389            range,
18390            owner_name_start_byte: name_node.start_byte(),
18391            namespace_component_count: namespace_components.len(),
18392            scope_components,
18393        });
18394    }
18395    WalkControl::Continue
18396}
18397
18398fn cpp_sentinel_extend_unique_owner_ranges(
18399    owners: &mut Vec<CppSentinelRecoveredOwner>,
18400    additional: Vec<CppSentinelRecoveredOwner>,
18401) {
18402    for owner in additional {
18403        if !owners.iter().any(|existing| {
18404            existing.range == owner.range && existing.scope_components == owner.scope_components
18405        }) {
18406            owners.push(owner);
18407        }
18408    }
18409}
18410
18411fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18412    if node.kind() != "ERROR" || node.named_child_count() != 1 {
18413        return false;
18414    }
18415    let Some(end_name) = node.named_child(0) else {
18416        return false;
18417    };
18418    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18419        return false;
18420    }
18421    let mut cursor = node.walk();
18422    node.children(&mut cursor)
18423        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18424}
18425
18426/// Collect owner definitions that the malformed sentinel left as later
18427/// declaration-list siblings. Parser-visible namespace siblings are a hard
18428/// boundary: their declarations must keep their own lexical namespace.
18429fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18430    parent: Node<'_>,
18431    sentinel_node: Node<'_>,
18432    namespace_components: &[String],
18433    source: &str,
18434) -> Vec<CppSentinelRecoveredOwner> {
18435    let mut owners = Vec::new();
18436    let mut after_sentinel = false;
18437    let mut cursor = parent.walk();
18438    for child in parent.named_children(&mut cursor) {
18439        if !after_sentinel {
18440            if same_node(child, sentinel_node) {
18441                after_sentinel = true;
18442            }
18443            continue;
18444        }
18445        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18446    }
18447    owners
18448}
18449
18450/// Collect owner definitions after a malformed namespace, stopping only at
18451/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
18452/// enclosing container is not trusted to belong to the recovered namespace.
18453fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18454    parent: Node<'_>,
18455    sentinel_node: Node<'_>,
18456    namespace_components: &[String],
18457    source: &str,
18458) -> Option<Vec<CppSentinelRecoveredOwner>> {
18459    let mut owners = Vec::new();
18460    let mut after_namespace = false;
18461    let mut cursor = parent.walk();
18462    for child in parent.named_children(&mut cursor) {
18463        if !after_namespace {
18464            if same_node(child, sentinel_node) {
18465                after_namespace = true;
18466            }
18467            continue;
18468        }
18469        if cpp_sentinel_namespace_end(child, source) {
18470            return Some(owners);
18471        }
18472        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18473    }
18474    None
18475}
18476
18477fn cpp_sentinel_append_owner_ranges(
18478    sibling: Node<'_>,
18479    namespace_components: &[String],
18480    source: &str,
18481    owners: &mut Vec<CppSentinelRecoveredOwner>,
18482) {
18483    walk_named_tree_preorder(sibling, true, |node| {
18484        if node.kind() == "namespace_definition" {
18485            return WalkControl::SkipChildren;
18486        }
18487        cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18488    });
18489}
18490
18491fn cpp_sentinel_recovered_sibling_owner_ranges(
18492    sentinel_node: Node<'_>,
18493    namespace_components: &[String],
18494    source: &str,
18495) -> Vec<CppSentinelRecoveredOwner> {
18496    let Some(declaration_list) = sentinel_node
18497        .parent()
18498        .filter(|parent| parent.kind() == "declaration_list")
18499    else {
18500        return Vec::new();
18501    };
18502    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18503        declaration_list,
18504        sentinel_node,
18505        namespace_components,
18506        source,
18507    );
18508
18509    let Some(namespace) = declaration_list
18510        .parent()
18511        .filter(|parent| parent.kind() == "namespace_definition")
18512    else {
18513        return owners;
18514    };
18515    let Some(outer_parent) = namespace.parent() else {
18516        return owners;
18517    };
18518    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18519        outer_parent,
18520        namespace,
18521        namespace_components,
18522        source,
18523    ) {
18524        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18525    }
18526    owners
18527}
18528
18529fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18530    let mut current = function_declarator.child_by_field_name("declarator")?;
18531    loop {
18532        if let Some(name) = macro_decorated_unqualified_name(current) {
18533            current = name;
18534            continue;
18535        }
18536        if matches!(
18537            current.kind(),
18538            "qualified_identifier"
18539                | "scoped_identifier"
18540                | "scoped_type_identifier"
18541                | "identifier"
18542                | "field_identifier"
18543                | "operator_name"
18544                | "destructor_name"
18545                | "literal_operator_name"
18546        ) {
18547            return Some(current);
18548        }
18549        current = current
18550            .child_by_field_name("declarator")
18551            .or_else(|| current.child_by_field_name("name"))
18552            .or_else(|| last_named_child(current))?;
18553    }
18554}
18555
18556/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
18557/// `::` between the halves. The scope is an attribute-like macro token, not
18558/// a namespace; `Name` is the declared name.
18559///
18560/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
18561/// tree-sitter joins the attribute macro and the constructor name into one
18562/// `qualified_identifier` and marks the separator it had to invent as MISSING.
18563/// That flag is the grammar's own record that no separator is in the source,
18564/// so read it instead of looking for `::` in the node text. A genuine
18565/// `Outer::Inner` keeps a present separator and is declined here, and so is
18566/// the explicit-global `::name`, whose `::` is present and whose scope is
18567/// absent.
18568fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18569    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18570        return None;
18571    }
18572    let mut cursor = node.walk();
18573    if node
18574        .children(&mut cursor)
18575        .any(|child| child.kind() == "::" && !child.is_missing())
18576    {
18577        return None;
18578    }
18579    node.child_by_field_name("name")
18580}
18581
18582fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18583    enum Component<'tree> {
18584        Node(Node<'tree>),
18585        Name(Node<'tree>),
18586    }
18587    let mut stack = vec![Component::Node(node)];
18588    let mut components = Vec::new();
18589    while let Some(component) = stack.pop() {
18590        let node = match component {
18591            Component::Name(node) => {
18592                components.push(canonical_cpp_qualified_component(
18593                    node.child_by_field_name("name")?,
18594                    source,
18595                )?);
18596                continue;
18597            }
18598            Component::Node(node) => node,
18599        };
18600        if let Some(name) = macro_decorated_unqualified_name(node) {
18601            stack.push(Component::Node(name));
18602            continue;
18603        }
18604        match node.kind() {
18605            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18606                stack.push(Component::Name(node));
18607                if let Some(scope) = node.child_by_field_name("scope") {
18608                    stack.push(Component::Node(scope));
18609                }
18610            }
18611            _ => components.push(canonical_cpp_qualified_component(node, source)?),
18612        }
18613    }
18614    Some(components)
18615}
18616
18617fn cpp_sentinel_fragment_boundary<'tree>(
18618    function: Node<'tree>,
18619    class_node: Node<'tree>,
18620    class_body: Node<'tree>,
18621    source: &str,
18622) -> Option<(Node<'tree>, Node<'tree>)> {
18623    let declaration_list = function.parent()?;
18624    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18625        return None;
18626    }
18627    let namespace = declaration_list.parent()?;
18628    if namespace.kind() != "namespace_definition"
18629        || namespace.child_by_field_name("body") != Some(declaration_list)
18630    {
18631        return None;
18632    }
18633    let mut cursor = declaration_list.walk();
18634    let closes = declaration_list
18635        .children(&mut cursor)
18636        .filter(|child| {
18637            !child.is_named()
18638                && child.kind() == "}"
18639                && child.start_byte() >= function.end_byte()
18640                && child.start_byte() > class_node.end_byte()
18641                && child.start_byte() > class_body.start_byte()
18642        })
18643        .collect::<Vec<_>>();
18644    let [close] = closes.as_slice() else {
18645        return None;
18646    };
18647    let semicolon = namespace.next_named_sibling()?;
18648    if !cpp_is_stray_semicolon(semicolon, source)
18649        || close.end_byte() != namespace.end_byte()
18650        || semicolon.start_byte() < namespace.end_byte()
18651    {
18652        return None;
18653    }
18654    Some((*close, semicolon))
18655}
18656
18657/// Detect the bogus declaration/function tree that tree-sitter recovers for a
18658/// region prefixed by an object-like macro sentinel the parser cannot see
18659/// (issue #941), and return the byte range `[start, end)` of the swallowed
18660/// declaration interior to reparse.
18661///
18662/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
18663/// `function_definition` whose first non-comment named child is the sentinel
18664/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
18665/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
18666/// the real items.
18667/// `start` is the end of the sentinel identifier -- everything after it is the
18668/// genuine source. `end` is the node's end, extended across any trailing empty
18669/// `;` statement the mis-parse displaced past the node (the class/struct closing
18670/// semicolon), so the reparse sees a complete, brace-balanced item.
18671///
18672/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
18673/// node (`has_error`), or -- under the macro-declaration grammar, which parses
18674/// the sentinel region without recovery -- carry the sentinel as a
18675/// `macro_modifier`, a production admitted only where the parse would
18676/// otherwise contain an error. Unknown annotation/export macros can make a
18677/// real callable error-recovered even though tree-sitter still preserves its
18678/// declarator, so a preserved callable is admitted only when a displaced class
18679/// keyword precedes that declarator. The clean-reparse-to-items gate in
18680/// `cpp_reparsed_items_are_indexable` is the final arbiter.
18681/// Return the reparse start and, when present, the structurally recovered class
18682/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
18683/// separately from the reparse start because an opaque template-declaration
18684/// macro may precede it.
18685fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18686    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18687        return None;
18688    }
18689    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
18690    // retain a valid function declarator despite the unknown export macro making
18691    // the outer node erroneous. Remember that declarator for the ordering gate
18692    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
18693    // class keyword precedes a spurious callable assembled from a later member.
18694    let mut declarator_cursor = node.walk();
18695    let preserved_callable = node
18696        .children_by_field_name("declarator", &mut declarator_cursor)
18697        .find_map(extract_function_declarator);
18698    // Leading documentation comments are attached to the malformed
18699    // `function_definition` as named children.  They are not part of the
18700    // sentinel prefix, so select the first non-comment child structurally
18701    // rather than requiring the sentinel to be child zero.  This is the shape
18702    // emitted for nlohmann/json's `basic_json`: its class documentation comment
18703    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
18704    // envelope otherwise ends at the first nested union.
18705    let mut cursor = node.walk();
18706    let first = node
18707        .named_children(&mut cursor)
18708        .find(|child| child.kind() != "comment")?;
18709    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
18710    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
18711    // the same token is wrapped in. The wrapper is itself the recovery
18712    // evidence the older grammar's `has_error` carried, so a clean envelope is
18713    // admitted only in that form. The macro-declaration grammar can also keep
18714    // the sentinel as the `type` and absorb the displaced head that follows it
18715    // (`BEGIN_NS TEMPLATE_DECLARATION class Box { ... }`) into a
18716    // `macro_modifier_declarator` chain, which carries the same evidence.
18717    let (first_end, sentinel) = if first.kind() == "type_identifier" {
18718        if !node.has_error()
18719            && node
18720                .child_by_field_name("declarator")
18721                .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18722        {
18723            return None;
18724        }
18725        (
18726            first.end_byte(),
18727            normalize_cpp_whitespace(node_text(first, source)),
18728        )
18729    } else {
18730        let identifier = macro_modifier_identifier(first)?;
18731        (
18732            first.end_byte(),
18733            normalize_cpp_whitespace(node_text(identifier, source)),
18734        )
18735    };
18736    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18737        return None;
18738    }
18739    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
18740    // makes the trailing sentinel of one region and the leading sentinel of the
18741    // next both land as bare macro-token identifiers ahead of the real content.
18742    // Advance past every leading macro-token identifier so the reparse begins at
18743    // genuine source rather than another sentinel that would re-form the bogus
18744    // shape and fail the reparse gate.
18745    let mut start = first_end;
18746    let mut after_first = false;
18747    let mut cursor = node.walk();
18748    for child in node.named_children(&mut cursor) {
18749        if !after_first {
18750            if same_node(child, first) {
18751                after_first = true;
18752            }
18753            continue;
18754        }
18755        let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18756            Some(child)
18757        } else {
18758            macro_modifier_identifier(child)
18759        };
18760        if macro_token.is_some_and(|token| {
18761            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18762        }) {
18763            start = child.end_byte();
18764        } else {
18765            break;
18766        }
18767    }
18768    // An additional opaque template-declaration macro before a class can be
18769    // folded into the bogus function's qualified declarator.  In that shape
18770    // the macro is not a direct sibling we can skip above; tree-sitter exposes
18771    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
18772    // Reparse from that keyword (or a real preceding `template` keyword) so the
18773    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
18774    // a class nested in a genuine sentinel-wrapped namespace lies after the
18775    // body opening and must not change the established region start.
18776    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18777    let mut class_start = None;
18778    let mut template_start = None;
18779    let mut stack = vec![node];
18780    while let Some(current) = stack.pop() {
18781        if current.start_byte() >= prefix_end {
18782            continue;
18783        }
18784        if matches!(
18785            current.kind(),
18786            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18787        ) {
18788            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18789                "class" | "struct" | "union" | "enum" => {
18790                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18791                        seen.min(current.start_byte())
18792                    }));
18793                }
18794                "template" => {
18795                    template_start =
18796                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
18797                            seen.min(current.start_byte())
18798                        }));
18799                }
18800                _ => {}
18801            }
18802        }
18803        let mut cursor = current.walk();
18804        stack.extend(current.children(&mut cursor));
18805    }
18806    if preserved_callable.is_some_and(|callable| {
18807        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18808    }) {
18809        return None;
18810    }
18811    if let Some(class_start) = class_start {
18812        start = template_start
18813            .filter(|template_start| *template_start < class_start)
18814            .unwrap_or(class_start);
18815    }
18816    Some((start, class_start))
18817}
18818
18819/// Locate a sentinel-prefixed class whose malformed declaration was split across
18820/// root-level siblings. The true class close is represented structurally as a
18821/// lone `}` error followed by the class's displaced `;`; nested method/body
18822/// errors are not direct siblings of the sentinel node and therefore cannot
18823/// satisfy this pair.
18824fn cpp_sentinel_macro_class_region<'tree>(
18825    node: Node<'tree>,
18826    source: &str,
18827) -> Option<(usize, usize, usize, usize, usize, usize)> {
18828    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18829        return None;
18830    };
18831    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18832        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18833        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18834    if class_start >= body_open_start {
18835        return None;
18836    }
18837    let sibling_close = {
18838        let mut sibling = node.next_named_sibling();
18839        let mut found = None;
18840        while let Some(current) = sibling {
18841            let next = current.next_named_sibling();
18842            if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18843                found = Some((
18844                    current.start_byte(),
18845                    semicolon.end_byte(),
18846                    semicolon.end_position().row + 1,
18847                ));
18848                break;
18849            }
18850            sibling = next;
18851        }
18852        found
18853    };
18854    // A stray `};` sibling is this class's close only when the bounded reparse
18855    // agrees the first body-bearing class ENDS there. When the malformed
18856    // envelope swallowed the class's true close, the scan can promote a much
18857    // later scope's close instead -- in protobuf-generated headers
18858    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
18859    // `struct TableStruct_*` paired with the first message class's `};`, making
18860    // the recovered "class body" span whole `namespace {}` blocks and minting
18861    // namespace-scope classes as nested members of the recovered class, which
18862    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
18863    // (#2275). On disagreement, fall through to the suffix-reparse boundary
18864    // below, which derives the close from the class node's own balanced body
18865    // range.
18866    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18867        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18868            return false;
18869        };
18870        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18871        // A region reparse is its own tree and needs its own parent index.
18872        let reparsed_ancestry = ParentIndex::new(tree.root_node());
18873        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18874            tree.root_node(),
18875            template_node,
18876            source,
18877            &reparsed_ancestry,
18878        ) else {
18879            return false;
18880        };
18881        let body = reparsed_class.body;
18882        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18883    });
18884    let (class_close_start, class_close_end, class_close_line) =
18885        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18886            (class_close_start, class_close_end, class_close_line)
18887        } else {
18888            // When the malformed envelope itself is an ERROR, tree-sitter can
18889            // leave the class's balanced close in the source while promoting
18890            // all following members to siblings. Reparse the complete suffix
18891            // and use the first body-bearing class node's own field range as
18892            // the partition boundary. This keeps balancing in tree-sitter and
18893            // preserves the source's original byte offsets.
18894            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18895            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18896            // A region reparse is its own tree and needs its own parent index.
18897            let reparsed_ancestry = ParentIndex::new(tree.root_node());
18898            let reparsed_class = cpp_sentinel_reparsed_class(
18899                tree.root_node(),
18900                template_node,
18901                source,
18902                &reparsed_ancestry,
18903            )?;
18904            let body = reparsed_class.body;
18905            let class_close_end = body.end_byte();
18906            let class_close_start = class_close_end.checked_sub(1)?;
18907            let class_close_line = body.end_position().row + 1;
18908            (class_close_start, class_close_end, class_close_line)
18909        };
18910    if class_close_start <= class_start {
18911        return None;
18912    }
18913
18914    // Reparse only far enough to expose the class body opening. This is a
18915    // structured check that the candidate really begins with a body-bearing
18916    // class-like item; the original malformed tree cannot provide that node.
18917    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18918    let class_root = tree.root_node();
18919    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18920    // A region reparse is its own tree and needs its own parent index.
18921    let reparsed_ancestry = ParentIndex::new(class_root);
18922    let reparsed_class =
18923        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18924    let body = reparsed_class.body;
18925    // The class body opening must agree with the malformed wrapper's structured
18926    // body field. This rejects an inner nested class while permitting later
18927    // members to remain fragmented as root-level siblings in the bounded parse.
18928    if body.start_byte() != body_open_start {
18929        return None;
18930    }
18931    let body_start = body.start_byte().checked_add(1)?;
18932    (body_start < class_close_start).then_some((
18933        reparse_start,
18934        class_start,
18935        body_start,
18936        class_close_start,
18937        class_close_end,
18938        class_close_line,
18939    ))
18940}
18941
18942/// Find the `{` token immediately following the class/struct/union/enum token
18943/// at `class_start` in the malformed tree. The token is anonymous in the C++
18944/// grammar, so this deliberately walks all children (not only named children)
18945/// and relies on sibling structure rather than source-text searching.
18946fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18947    let mut stack = vec![node];
18948    while let Some(current) = stack.pop() {
18949        if current.start_byte() == class_start
18950            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18951        {
18952            // The macro-declaration grammar keeps the class whole: the token's
18953            // specifier ancestor owns the body, and its `{` is the body's
18954            // first child rather than a later sibling of the token.
18955            let mut ancestor = current.parent();
18956            while let Some(parent) = ancestor {
18957                if matches!(
18958                    parent.kind(),
18959                    "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18960                ) {
18961                    if let Some(body) = cpp_body_node(parent) {
18962                        return body
18963                            .child(0)
18964                            .filter(|open| open.kind() == "{")
18965                            .map(|open| open.start_byte());
18966                    }
18967                    break;
18968                }
18969                if matches!(parent.kind(), "ERROR" | "declaration") {
18970                    break;
18971                }
18972                ancestor = parent.parent();
18973            }
18974            let mut sibling = current.next_sibling();
18975            while let Some(candidate) = sibling {
18976                if candidate.kind() == "{" {
18977                    return Some(candidate.start_byte());
18978                }
18979                sibling = candidate.next_sibling();
18980            }
18981        }
18982        let mut cursor = current.walk();
18983        stack.extend(current.children(&mut cursor));
18984    }
18985    None
18986}
18987
18988/// The class body that tree-sitter displaced out of a sentinel-prefixed
18989/// declaration and left as the malformed node's next sibling.
18990///
18991/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
18992/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
18993/// the last child of that `ERROR` and its `{` opens a sibling
18994/// `compound_statement` instead. The body is still the malformed tree's own
18995/// structured token, which is what the caller's `body.start_byte() !=
18996/// body_open_start` agreement check needs; it just is not reachable by walking
18997/// forward from the class token inside the node.
18998fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
18999    node.next_named_sibling()
19000        .filter(|sibling| sibling.kind() == "compound_statement")
19001}
19002
19003fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
19004    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
19005    let mut end = if class_start.is_some() {
19006        cpp_macro_prefixed_class_end(source, start)?
19007    } else {
19008        node.end_byte()
19009    };
19010    if class_start.is_none()
19011        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
19012    {
19013        end = end.max(namespace_end);
19014    }
19015    let mut sibling = node.next_named_sibling();
19016    while let Some(current) = sibling {
19017        if !cpp_is_stray_semicolon(current, source) {
19018            break;
19019        }
19020        end = current.end_byte();
19021        sibling = current.next_named_sibling();
19022    }
19023    (start < end).then_some((start, end))
19024}
19025
19026/// Extend a sentinel reparse through a following namespace that tree-sitter
19027/// flattened into the sentinel node's sibling list.
19028///
19029/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
19030/// unknown macro becomes a false function return type and consumes the first
19031/// namespace body. A second `namespace detail` then loses its enclosing node:
19032/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
19033/// attaches its declarations to the surrounding error tree. Reparse from that
19034/// structured keyword so tree-sitter, rather than a source-text brace scan,
19035/// supplies the complete namespace boundary.
19036fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
19037    let mut sibling = node.next_sibling();
19038    let keyword = loop {
19039        let candidate = sibling?;
19040        sibling = candidate.next_sibling();
19041        if candidate.kind() != "comment" {
19042            break candidate;
19043        }
19044    };
19045    if keyword.kind() != "namespace" {
19046        return None;
19047    }
19048    let name = loop {
19049        let candidate = sibling?;
19050        sibling = candidate.next_sibling();
19051        if candidate.kind() != "comment" {
19052            break candidate;
19053        }
19054    };
19055    if cpp_namespace_name_components(name, source).is_empty() {
19056        return None;
19057    }
19058    let open = loop {
19059        let candidate = sibling?;
19060        sibling = candidate.next_sibling();
19061        if candidate.kind() != "comment" {
19062            break candidate;
19063        }
19064    };
19065    if open.kind() != "{" {
19066        return None;
19067    }
19068
19069    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
19070    let root = tree.root_node();
19071    let mut cursor = root.walk();
19072    let namespace = root
19073        .named_children(&mut cursor)
19074        .find(|candidate| candidate.kind() != "comment")?;
19075    (namespace.kind() == "namespace_definition"
19076        && namespace.start_byte() == keyword.start_byte()
19077        && namespace.child_by_field_name("body").is_some())
19078    .then_some(namespace.end_byte())
19079}
19080
19081/// Parse the source suffix beginning at a structurally recovered class/template
19082/// keyword and return the end of its first body-bearing class item.  The parser,
19083/// rather than a brace scanner, owns nested-body balancing.  This is needed when
19084/// the original error tree truncates the class and scatters later members as
19085/// top-level siblings.
19086fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
19087    let tree = cpp_reparse_region_items(source, start, source.len())?;
19088    let root = tree.root_node();
19089    let mut cursor = root.walk();
19090    for item in root.named_children(&mut cursor) {
19091        if item.end_byte() <= start || item.kind() == "comment" {
19092            continue;
19093        }
19094        let mut stack = vec![item];
19095        while let Some(current) = stack.pop() {
19096            if matches!(
19097                current.kind(),
19098                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19099            ) && cpp_body_node(current).is_some()
19100            {
19101                return Some(current.end_byte());
19102            }
19103            let mut cursor = current.walk();
19104            stack.extend(current.named_children(&mut cursor));
19105        }
19106        // The recovered prefix is required to begin with the class item.  If
19107        // the first real item is something else, fail closed rather than skip
19108        // arbitrary source looking for a later class.
19109        return None;
19110    }
19111    None
19112}
19113
19114/// An empty `;` statement: the displaced closing semicolon of a struct/class that
19115/// the sentinel mis-parse split off past the bogus function node.
19116fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
19117    node.kind() == "expression_statement"
19118        && node.named_child_count() == 0
19119        && node_text(node, source).trim() == ";"
19120}
19121
19122fn cpp_stray_close_followed_by_semicolon<'tree>(
19123    close: Node<'tree>,
19124    next: Option<Node<'tree>>,
19125    source: &str,
19126) -> Option<Node<'tree>> {
19127    cpp_is_stray_close_brace(close, source)
19128        .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
19129        .flatten()
19130}
19131
19132/// Recover the member that follows CPython's object header macro when
19133/// tree-sitter folds both declarations into one malformed field.
19134///
19135/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
19136/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
19137/// sole child is the real field declarator. The tail may wrap that identifier
19138/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
19139/// malformed wrappers do not provide the same evidence that this is the
19140/// CPython macro boundary.
19141#[derive(Clone, Copy)]
19142pub(crate) struct RecoveredPyObjectHeadField<'tree> {
19143    pub(crate) type_node: Node<'tree>,
19144    pub(crate) name: Node<'tree>,
19145    pub(crate) declarator: Node<'tree>,
19146}
19147
19148impl RecoveredPyObjectHeadField<'_> {
19149    pub(crate) fn pointer_depth(self) -> i32 {
19150        let mut depth = 0;
19151        let mut current = self.declarator;
19152        while current != self.name {
19153            debug_assert_eq!(current.kind(), "pointer_declarator");
19154            depth += 1;
19155            current = current
19156                .child_by_field_name("declarator")
19157                .expect("recovered PyObject field pointer has an inner declarator");
19158        }
19159        depth
19160    }
19161}
19162
19163pub(crate) fn recovered_pyobject_head_field<'tree>(
19164    node: Node<'tree>,
19165    source: &str,
19166) -> Option<RecoveredPyObjectHeadField<'tree>> {
19167    if node.kind() != "field_declaration" {
19168        return None;
19169    }
19170    let type_node = node.child_by_field_name("type")?;
19171    if type_node.kind() != "type_identifier"
19172        || node_text(type_node, source).trim() != "PyObject_HEAD"
19173    {
19174        return None;
19175    }
19176    let pseudo_declarator = node.child_by_field_name("declarator")?;
19177    let mut cursor = node.walk();
19178    let errors = node
19179        .named_children(&mut cursor)
19180        .filter(|child| child.kind() == "ERROR")
19181        .collect::<Vec<_>>();
19182    let [error] = errors.as_slice() else {
19183        return None;
19184    };
19185    if error.named_child_count() != 1 {
19186        return None;
19187    }
19188    let error_child = error.named_child(0)?;
19189    if pseudo_declarator.kind() == "field_identifier"
19190        && error.start_byte() >= pseudo_declarator.end_byte()
19191        && error_child.kind() == "identifier"
19192    {
19193        return Some(RecoveredPyObjectHeadField {
19194            type_node: pseudo_declarator,
19195            name: error_child,
19196            declarator: error_child,
19197        });
19198    }
19199    if pseudo_declarator.kind() != "pointer_declarator"
19200        || error.end_byte() > pseudo_declarator.start_byte()
19201        || error_child.kind() != "identifier"
19202    {
19203        return None;
19204    }
19205    let mut name = pseudo_declarator;
19206    while name.kind() == "pointer_declarator" {
19207        name = name.child_by_field_name("declarator")?;
19208    }
19209    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19210        type_node: error_child,
19211        name,
19212        declarator: pseudo_declarator,
19213    })
19214}
19215
19216/// Recover the real field name when a leading object-like annotation macro
19217/// displaces a qualified type into tree-sitter's bit-field recovery shape.
19218///
19219/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
19220/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
19221/// `bitfield_clause` containing an error plus an assignment.  The assignment's
19222/// left field is the only structured declaration name in that malformed tail.
19223/// A real bit-field is excluded by the all-caps macro type and required error.
19224fn recovered_macro_qualified_field_clause<'tree>(
19225    node: Node<'tree>,
19226    source: &str,
19227) -> Option<Node<'tree>> {
19228    if node.kind() != "field_declaration" {
19229        return None;
19230    }
19231    let macro_type = node.child_by_field_name("type")?;
19232    if macro_type.kind() != "type_identifier"
19233        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19234    {
19235        return None;
19236    }
19237    let mut cursor = node.walk();
19238    node.named_children(&mut cursor)
19239        .find(|child| child.kind() == "bitfield_clause")
19240}
19241
19242fn recovered_macro_qualified_field_declarators<'tree>(
19243    node: Node<'tree>,
19244    source: &str,
19245) -> Option<Vec<Node<'tree>>> {
19246    let pseudo_declarator = node.child_by_field_name("declarator")?;
19247    if pseudo_declarator.kind() != "field_identifier" {
19248        return None;
19249    }
19250    let clause = recovered_macro_qualified_field_clause(node, source)?;
19251    if !(0..clause.named_child_count()).any(|index| {
19252        clause
19253            .named_child(index)
19254            .is_some_and(|child| child.kind() == "ERROR")
19255    }) {
19256        return None;
19257    }
19258    let mut recovered = Vec::new();
19259    let mut stack = vec![clause];
19260    while let Some(current) = stack.pop() {
19261        if current.kind() == "assignment_expression"
19262            && let Some(left) = current.child_by_field_name("left")
19263            && extract_variable_name(left, source).is_some()
19264        {
19265            recovered.push(left);
19266            break;
19267        }
19268        let mut cursor = current.walk();
19269        stack.extend(current.named_children(&mut cursor));
19270    }
19271    if recovered.is_empty() {
19272        return None;
19273    }
19274    let mut cursor = node.walk();
19275    recovered.extend(
19276        node.children_by_field_name("declarator", &mut cursor)
19277            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19278    );
19279    Some(recovered)
19280}
19281
19282/// Recover a macro-qualified constructor that tree-sitter represents as one
19283/// field declaration. The constructor call remains inside the direct recovery
19284/// error, while each member initializer becomes a false function declarator.
19285/// The class owner proves the constructor name and lets the caller ignore those
19286/// initializer declarators.
19287fn recovered_macro_qualified_constructor_call<'tree>(
19288    node: Node<'tree>,
19289    class_name: &str,
19290    source: &str,
19291) -> Option<Node<'tree>> {
19292    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19293    let error = bitfield
19294        .named_child(0)
19295        .filter(|child| child.kind() == "ERROR")?;
19296    let mut stack = vec![error];
19297    while let Some(current) = stack.pop() {
19298        if current.kind() == "call_expression"
19299            && current
19300                .child_by_field_name("function")
19301                .is_some_and(|function| node_text(function, source) == class_name)
19302            && current
19303                .child_by_field_name("arguments")
19304                .is_some_and(|arguments| arguments.kind() == "argument_list")
19305        {
19306            return Some(current);
19307        }
19308        let mut cursor = current.walk();
19309        stack.extend(current.named_children(&mut cursor));
19310    }
19311    None
19312}
19313
19314/// The constructor head of a macro-decorated constructor that the
19315/// macro-declaration grammar reads as one field declaration
19316/// (`MSC_WARNING(suppress : 26495) CONSTEXPR Buffer(grow_fun grow) noexcept
19317/// : size_(size), grow_(grow) {}`, #1838). The macro is the field's `type`,
19318/// the constructor head is its first declarator (possibly inside a
19319/// `macro_modifier_declarator`), the first member initializer is a
19320/// `bitfield_clause` call, every later initializer is another function
19321/// declarator, and the body is the `default_value` initializer list. A
19322/// constructor declares no type, and no real field pairs a callable declarator
19323/// with a bitfield, so a head naming the enclosing class beside a bitfield
19324/// call is this shape. The caller publishes the head and none of the
19325/// initializer declarators.
19326fn recovered_macro_initialized_constructor_declarator<'tree>(
19327    node: Node<'tree>,
19328    class_name: &str,
19329    source: &str,
19330) -> Option<Node<'tree>> {
19331    if node.kind() != "field_declaration" {
19332        return None;
19333    }
19334    let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19335    if cpp_function_declarator_name_node(constructor)
19336        .is_none_or(|name| node_text(name, source) != class_name)
19337    {
19338        return None;
19339    }
19340    let mut cursor = node.walk();
19341    let initializer = node
19342        .named_children(&mut cursor)
19343        .find(|child| child.kind() == "bitfield_clause")?
19344        .named_child(0)?;
19345    (initializer.kind() == "call_expression").then_some(constructor)
19346}
19347
19348/// Recover a macro-qualified member function declaration that tree-sitter
19349/// represents as a pseudo-field. An object-like export macro before a qualified
19350/// return type can displace the namespace and type into an ERROR/bitfield
19351/// recovery, leaving the callable as a structured `call_expression`.
19352///
19353/// The caller must route this shape before ordinary declarator classification;
19354/// otherwise the displaced namespace identifier is published as a field.
19355fn recovered_macro_qualified_function_call<'tree>(
19356    node: Node<'tree>,
19357    source: &str,
19358) -> Option<Node<'tree>> {
19359    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19360    let declarator = node.child_by_field_name("declarator")?;
19361    if declarator.kind() != "field_identifier" {
19362        return None;
19363    }
19364    let mut cursor = node.walk();
19365    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19366    if !named.iter().any(|child| {
19367        child.kind() == "storage_class_specifier"
19368            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19369    }) {
19370        return None;
19371    }
19372    let mut bitfield_cursor = bitfield.walk();
19373    let payload = bitfield
19374        .named_children(&mut bitfield_cursor)
19375        .collect::<Vec<_>>();
19376    let [displaced_error, call] = payload.as_slice() else {
19377        return None;
19378    };
19379    if displaced_error.kind() != "ERROR"
19380        || displaced_error.named_child_count() != 1
19381        || displaced_error
19382            .named_child(0)
19383            .is_none_or(|child| child.kind() != "identifier")
19384        || call.kind() != "call_expression"
19385        || call
19386            .child_by_field_name("function")
19387            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19388        || call
19389            .child_by_field_name("arguments")
19390            .is_none_or(|arguments| arguments.kind() != "argument_list")
19391    {
19392        return None;
19393    }
19394    Some(*call)
19395}
19396
19397/// The real constructor declarator of a macro-decorated constructor the
19398/// macro-declaration grammar reads as an explicit specifier, an `ERROR`
19399/// holding the macro and the constructor head as a `macro_modifier_declarator`,
19400/// and the member initializer mis-read as the function's own declarator
19401/// (`explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}`, #2552 shape 1).
19402/// The constructor head inside the `ERROR` is what names the class; the
19403/// declarator the `function_definition` fields is the initializer, never the
19404/// member.
19405fn recovered_macro_decorated_constructor_declarator<'tree>(
19406    node: Node<'tree>,
19407    class_name: &str,
19408    source: &str,
19409) -> Option<Node<'tree>> {
19410    if node.kind() != "function_definition" {
19411        return None;
19412    }
19413    let mut cursor = node.walk();
19414    let mut heads = Vec::new();
19415    for error in node
19416        .named_children(&mut cursor)
19417        .filter(|child| child.kind() == "ERROR")
19418    {
19419        let mut stack = vec![error];
19420        while let Some(current) = stack.pop() {
19421            let is_constructor_head = match current.kind() {
19422                "macro_modifier_declarator" | "function_declarator" => {
19423                    extract_function_declarator(current).is_some_and(|declarator| {
19424                        cpp_function_declarator_name_node(declarator)
19425                            .is_some_and(|name| node_text(name, source) == class_name)
19426                    })
19427                }
19428                _ => false,
19429            };
19430            if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19431                heads.push(declarator);
19432                continue;
19433            }
19434            let mut cursor = current.walk();
19435            stack.extend(current.named_children(&mut cursor));
19436        }
19437    }
19438    let [head] = heads.as_slice() else {
19439        return None;
19440    };
19441    Some(*head)
19442}
19443
19444fn recovered_macro_qualified_function_parameters(
19445    arguments: Node<'_>,
19446    source: &str,
19447) -> Option<(String, Vec<String>, [u8; 32])> {
19448    use brokk_bifrost_core::analyzer::tree_walk::{
19449        overload_discriminator, overload_parameter_tokens,
19450    };
19451    if arguments.kind() != "argument_list" {
19452        return None;
19453    }
19454    let mut cursor = arguments.walk();
19455    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19456    if named.is_empty() {
19457        return Some((
19458            "()".to_string(),
19459            Vec::new(),
19460            overload_discriminator(Vec::<String>::new()),
19461        ));
19462    }
19463    let mut types = Vec::new();
19464    let mut tokens = Vec::new();
19465    let mut labels = Vec::new();
19466    let mut index = 0;
19467    while index < named.len() {
19468        let parameter_type = named[index];
19469        let parameter_name = named.get(index + 1).copied()?;
19470        if !matches!(
19471            parameter_type.kind(),
19472            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19473        ) || parameter_name.kind() != "ERROR"
19474            || parameter_name.named_child_count() != 1
19475            || parameter_name
19476                .named_child(0)
19477                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19478        {
19479            return None;
19480        }
19481        let parameter_name = parameter_name.named_child(0)?;
19482        tokens.extend(overload_parameter_tokens(Some(parameter_type), source));
19483        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19484        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19485        index += 2;
19486    }
19487    Some((
19488        format!("({})", types.join(", ")),
19489        labels,
19490        overload_discriminator(tokens),
19491    ))
19492}
19493
19494/// Recognize the phantom field tree-sitter emits for a macro-qualified
19495/// function return type.  For example,
19496/// `static API result_type ThresholdForSmallA() { ... }` can become a
19497/// `field_declaration` (`API` as the type and `result_type` as a field name)
19498/// followed by a clean `function_definition` for `ThresholdForSmallA`.
19499///
19500/// Keep this predicate entirely tied to the CST envelope: the type must be an
19501/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
19502/// the declaration must carry a missing semicolon rather than a real one, and
19503/// the immediate named sibling must expose a function declarator.  A real
19504/// macro-decorated field with an explicit semicolon therefore remains a field.
19505pub fn recovered_macro_return_type_node<'tree>(
19506    node: Node<'tree>,
19507    source: &str,
19508) -> Option<Node<'tree>> {
19509    if node.kind() != "field_declaration" {
19510        return None;
19511    }
19512    let macro_type = node.child_by_field_name("type")?;
19513    if macro_type.kind() != "type_identifier"
19514        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19515    {
19516        return None;
19517    }
19518    let declarator = node.child_by_field_name("declarator")?;
19519    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19520        return None;
19521    }
19522    let mut has_missing_semicolon = false;
19523    let mut has_real_semicolon = false;
19524    for child in children_iter(node) {
19525        if child.kind() != ";" {
19526            continue;
19527        }
19528        if child.is_missing() {
19529            has_missing_semicolon = true;
19530        } else {
19531            has_real_semicolon = true;
19532        }
19533    }
19534    if !has_missing_semicolon || has_real_semicolon {
19535        return None;
19536    }
19537    let mut next = node.next_named_sibling();
19538    while next.is_some_and(|sibling| sibling.kind() == "comment") {
19539        next = next.and_then(|sibling| sibling.next_named_sibling());
19540    }
19541    let next = next?;
19542    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19543        return None;
19544    }
19545    let function_declarator = next.child_by_field_name("declarator")?;
19546    extract_function_declarator(function_declarator).map(|_| declarator)
19547}
19548
19549/// The omitted-semicolon class field the macro-declaration grammar folds into
19550/// the head of the normally typed function that follows it. The older grammar
19551/// left `DWORD preserved_field` ahead of `void PreserveFollowingFunction() {}`
19552/// as a damaged `field_declaration`; the macro-declaration grammar reads the
19553/// same pair as two leading `macro_modifier`s of the clean function
19554/// definition. Only an exact modifier pair ahead of a real type field and a
19555/// function declarator is a field: a lone modifier ahead of the type is the
19556/// macro-return shape (`static API result_type f() { ... }`), which carries
19557/// no field.
19558fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19559    if node.kind() != "function_definition" {
19560        return None;
19561    }
19562    let type_node = node.child_by_field_name("type")?;
19563    let declarator = node.child_by_field_name("declarator")?;
19564    extract_function_declarator(declarator)?;
19565    let mut cursor = node.walk();
19566    let head = node
19567        .named_children(&mut cursor)
19568        .take_while(|child| *child != type_node)
19569        .filter(|child| child.kind() != "comment")
19570        .collect::<Vec<_>>();
19571    let [type_modifier, name_modifier] = head.as_slice() else {
19572        return None;
19573    };
19574    macro_modifier_identifier(*type_modifier)?;
19575    macro_modifier_identifier(*name_modifier)?;
19576    Some((*type_modifier, *name_modifier))
19577}
19578
19579/// The stand-in field evidence in a clean macro-return function definition:
19580/// `CUTLASS_DEVICE static OperandLayout Layout() { ... }` has a
19581/// `macro_modifier` head and a return type naming an active template type
19582/// parameter. The older grammar read the same head as a phantom
19583/// `field_declaration` named after the type, and dependent calls like
19584/// `OperandLayout::packed` resolved to that field (#1129). Returns the head's
19585/// first token and the type node; the active-parameter gate is the same one
19586/// the phantom-field suppression used, so a macro return type naming no
19587/// template parameter (`result_type`) recovers nothing.
19588fn macro_return_template_parameter_field<'tree>(
19589    node: Node<'tree>,
19590    source: &str,
19591    ancestry: &ParentIndex<'tree>,
19592) -> Option<(Node<'tree>, Node<'tree>)> {
19593    if node.kind() != "function_definition" {
19594        return None;
19595    }
19596    let type_node = node.child_by_field_name("type")?;
19597    if type_node.kind() != "type_identifier" {
19598        return None;
19599    }
19600    let declarator = node.child_by_field_name("declarator")?;
19601    extract_function_declarator(declarator)?;
19602    let mut cursor = node.walk();
19603    let head = node
19604        .named_children(&mut cursor)
19605        .take_while(|child| *child != type_node)
19606        .filter(|child| child.kind() != "comment")
19607        .collect::<Vec<_>>();
19608    let [first, ..] = head.as_slice() else {
19609        return None;
19610    };
19611    if !head
19612        .iter()
19613        .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19614        || !head.iter().any(|child| child.kind() == "macro_modifier")
19615    {
19616        return None;
19617    }
19618    let name = normalize_cpp_whitespace(node_text(type_node, source));
19619    if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19620        return None;
19621    }
19622    Some((*first, type_node))
19623}
19624
19625/// Whether `name` is a type parameter of a template declaration that lexically
19626/// encloses `node`. The malformed macro-return field uses the parameter name as
19627/// its pseudo-declarator; preserving that field is necessary to publish a
19628/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
19629/// ancestors instead of interpreting source text so nested templates and
19630/// parser-recovered regions retain their real lexical scopes.
19631pub(crate) fn cpp_active_template_type_parameter<'tree>(
19632    node: Node<'tree>,
19633    name: &str,
19634    source: &str,
19635    ancestry: &ParentIndex<'tree>,
19636) -> bool {
19637    let mut ancestor = ancestry.parent(node);
19638    while let Some(current) = ancestor {
19639        if current.kind() == "template_declaration"
19640            && let Some(parameters) = current.child_by_field_name("parameters")
19641            && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19642                cpp_template_parameter_name(parameter, source)
19643            })
19644            .type_parameter
19645        {
19646            return true;
19647        }
19648        ancestor = ancestry.parent(current);
19649    }
19650    false
19651}
19652
19653/// Reparse the region `[start, end)` of `source` as C++, confined to the region
19654/// via included ranges so every reparsed node keeps its original byte offset and
19655/// line number. The existing visitors read node text from the original source,
19656/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
19657/// `parse_rust_region_tree` technique.
19658fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19659    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19660}
19661
19662fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19663    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19664        return None;
19665    }
19666    let semicolon = node.next_sibling()?;
19667    if semicolon.kind() != ";" || semicolon.is_missing() {
19668        return None;
19669    }
19670    let row = node.start_position().row;
19671    let mut start = node.start_byte();
19672    let mut sibling = node.prev_sibling();
19673    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19674        if previous.kind() == ";" {
19675            break;
19676        }
19677        start = previous.start_byte();
19678        sibling = previous.prev_sibling();
19679    }
19680    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19681}
19682
19683/// One `RET name _(( args ));`-style K&R prototype-macro invocation
19684/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
19685/// byte spans a clean reparse needs, in original-source order.
19686struct PrototypeMacroCandidate {
19687    /// Where the return type and declared name begin.
19688    run_start: usize,
19689    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
19690    /// exclusive end of the first reparse span.
19691    identifier_start: usize,
19692    /// The inner `(`'s start byte: the start of the second reparse span.
19693    /// This paren is kept so the reparse sees exactly one parameter-list
19694    /// paren pair.
19695    inner_open_start: usize,
19696    /// Byte just past the inner `)`: the end of the second reparse span.
19697    inner_close_end: usize,
19698    /// Byte just past the outer `)`: the start of the third reparse span.
19699    outer_close_end: usize,
19700    /// Byte just past the terminating `;`: the end of the third reparse
19701    /// span, and of the whole candidate.
19702    semicolon_end: usize,
19703}
19704
19705impl PrototypeMacroCandidate {
19706    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
19707    /// one included-range tree: the return type and name, the parenthesized
19708    /// argument list, and the terminating `;`. The macro token and its outer
19709    /// wrapping parenthesis are omitted -- deleting them is exactly what the
19710    /// (absent) preprocessor macro expansion would do.
19711    fn ranges(&self) -> [(usize, usize); 3] {
19712        [
19713            (self.run_start, self.identifier_start),
19714            (self.inner_open_start, self.inner_close_end),
19715            (self.outer_close_end, self.semicolon_end),
19716        ]
19717    }
19718}
19719
19720/// The class a class-body prototype-macro reparse keeps around the member
19721/// (#6902): the class head through its `{`, the access label the member is
19722/// written under, and the class's `}`, at their original offsets. In that
19723/// parse the recovered prototype is a real member of a real class body, so its
19724/// `static` and its place on the body's access ladder state its modifiers
19725/// exactly as an ordinary member's do.
19726struct PrototypeMacroClassEnvelope<'tree> {
19727    /// The class's enclosing node, which is the recovery's origin.
19728    class_parent: Node<'tree>,
19729    /// The class head through the body's `{`.
19730    head: (usize, usize),
19731    /// The body's `{`, which finds the reparsed body.
19732    open: (usize, usize),
19733    /// The last `access_specifier` the body writes before the member, through
19734    /// its `:`.
19735    label: Option<(usize, usize)>,
19736    /// The body's `}`.
19737    close: (usize, usize),
19738}
19739
19740/// The [`PrototypeMacroClassEnvelope`] of one class-body member, read forward
19741/// with one cursor as [`cpp_member_declared_visibility`] reads the access
19742/// ladder. A member nested in anything but preprocessor conditionals, or under
19743/// a label a macro invocation swallowed, has no envelope that reproduces its
19744/// position, so it stays unrecovered rather than taking another access.
19745fn cpp_prototype_macro_class_envelope<'tree>(
19746    member: Node<'tree>,
19747    source: &str,
19748    ancestry: &ParentIndex<'tree>,
19749) -> Option<PrototypeMacroClassEnvelope<'tree>> {
19750    let mut list_level = member;
19751    let body = loop {
19752        let parent = ancestry.parent(list_level)?;
19753        if parent.kind() == "field_declaration_list" {
19754            break parent;
19755        }
19756        if !is_cpp_preprocessor_conditional(parent) {
19757            return None;
19758        }
19759        list_level = parent;
19760    };
19761    let class_like = ancestry.parent(body)?;
19762    let class_parent = ancestry.parent(class_like)?;
19763    let open = body.child(0).filter(|open| open.kind() == "{")?;
19764    let close = body
19765        .child(body.child_count().checked_sub(1)?)
19766        .filter(|close| close.kind() == "}" && !close.is_missing())?;
19767    let mut label = None;
19768    let mut under_swallowed_label = false;
19769    let mut cursor = body.walk();
19770    for child in body.children(&mut cursor) {
19771        if child == list_level {
19772            break;
19773        }
19774        if child.kind() == "access_specifier" {
19775            // The grammar leaves the label's `:` to the body.
19776            let colon = child
19777                .next_sibling()
19778                .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
19779            label = Some((child.start_byte(), colon.end_byte()));
19780            under_swallowed_label = false;
19781        } else if cpp_macro_swallowed_access_label(child, source).is_some() {
19782            under_swallowed_label = true;
19783        }
19784    }
19785    if under_swallowed_label {
19786        return None;
19787    }
19788    Some(PrototypeMacroClassEnvelope {
19789        class_parent,
19790        head: (class_like.start_byte(), open.end_byte()),
19791        open: (open.start_byte(), open.end_byte()),
19792        label,
19793        close: (close.start_byte(), close.end_byte()),
19794    })
19795}
19796
19797/// A live terminating semicolon owned directly by `node`.
19798fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19799    node.child(node.child_count().checked_sub(1)?)
19800        .filter(|child| child.kind() == ";" && !child.is_missing())
19801}
19802
19803/// The known pre-ANSI prototype macros whose expansion is exactly their one
19804/// argument. The malformed CST cannot prove that an arbitrary identifier has
19805/// that definition, so unknown spellings fail closed rather than turning an
19806/// ordinary broken declaration into a Function (issue #2932).
19807fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19808    matches!(
19809        node.kind(),
19810        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19811    ) && matches!(
19812        normalize_cpp_whitespace(node_text(node, source)).as_str(),
19813        "_" | "__P" | "OF" | "PROTO"
19814    )
19815}
19816
19817/// Read tree-sitter's recovery for `declared_name MACRO`: a
19818/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
19819/// declared name, and whose `name` is a known prototype macro.
19820fn cpp_prototype_macro_qualified_parts<'tree>(
19821    node: Node<'tree>,
19822    source: &str,
19823) -> Option<(Node<'tree>, Node<'tree>)> {
19824    if node.kind() != "qualified_identifier" {
19825        return None;
19826    }
19827    let declared_name = node
19828        .child_by_field_name("scope")
19829        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19830    let macro_name = macro_decorated_unqualified_name(node)?;
19831    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19832}
19833
19834/// The inner parenthesized node of tree-sitter's structured
19835/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
19836/// the macro invocation's outer parentheses while retaining the one pair an
19837/// ordinary function declarator needs. No delimiter search is involved: both
19838/// pairs and their ownership come from the CST.
19839fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19840    if arguments.kind() != "argument_list"
19841        || arguments.named_child_count() != 1
19842        || arguments.child_count() != 3
19843        || arguments
19844            .child(0)
19845            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19846        || arguments
19847            .child(2)
19848            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19849    {
19850        return None;
19851    }
19852    let inner = arguments.named_child(0)?;
19853    let close_index = match inner.kind() {
19854        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19855        // tree-sitter completes the C++ cast production with a zero-width
19856        // value after the live close parenthesis.
19857        "cast_expression" => inner.child_count().checked_sub(2)?,
19858        _ => return None,
19859    };
19860    (inner
19861        .child(0)
19862        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19863        && inner
19864            .child(close_index)
19865            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19866    .then_some(inner)
19867}
19868
19869fn cpp_prototype_macro_candidate_from_init_declaration(
19870    declaration: Node<'_>,
19871    source: &str,
19872) -> Option<PrototypeMacroCandidate> {
19873    let init = declaration
19874        .child_by_field_name("declarator")
19875        .filter(|declarator| declarator.kind() == "init_declarator")?;
19876    let malformed_declarator = init.child_by_field_name("declarator")?;
19877    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19878        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19879    } else {
19880        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19881            return None;
19882        }
19883        let declared_name_error = init
19884            .prev_named_sibling()
19885            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19886        let declared_name = declared_name_error
19887            .named_child(0)
19888            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19889        (declared_name, malformed_declarator)
19890    };
19891    let arguments = init
19892        .child_by_field_name("value")
19893        .filter(|value| value.kind() == "argument_list")?;
19894    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19895    let semicolon = cpp_direct_semicolon(declaration)?;
19896    let return_type = declaration.child_by_field_name("type")?;
19897    if return_type.end_byte() > declared_name.start_byte()
19898        || declared_name.end_byte() > macro_name.start_byte()
19899        || macro_name.end_byte() > arguments.start_byte()
19900        || arguments.end_byte() > semicolon.start_byte()
19901    {
19902        return None;
19903    }
19904    Some(cpp_prototype_macro_candidate_from_parts(
19905        declaration.start_byte(),
19906        macro_name,
19907        inner,
19908        arguments,
19909        semicolon,
19910    ))
19911}
19912
19913fn cpp_prototype_macro_candidate_from_qualified_declaration(
19914    declaration: Node<'_>,
19915    source: &str,
19916) -> Option<PrototypeMacroCandidate> {
19917    let qualified = declaration
19918        .child_by_field_name("declarator")
19919        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19920    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19921    let open_error = qualified
19922        .next_named_sibling()
19923        .filter(|next| next.kind() == "ERROR")?;
19924    let close_error = last_named_child(declaration)
19925        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19926    if open_error.child_count() < 3
19927        || open_error
19928            .child(0)
19929            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19930        || open_error
19931            .child(1)
19932            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19933        || close_error.child_count() != 2
19934        || close_error
19935            .child(0)
19936            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19937        || close_error
19938            .child(1)
19939            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19940    {
19941        return None;
19942    }
19943    let inner_open = open_error.child(1)?;
19944    let inner_close = close_error.child(0)?;
19945    let outer_close = close_error.child(1)?;
19946    let semicolon = cpp_direct_semicolon(declaration)?;
19947    let return_type = declaration.child_by_field_name("type")?;
19948    if return_type.end_byte() > qualified.start_byte()
19949        || macro_name.end_byte() > open_error.start_byte()
19950        || inner_open.start_byte() > inner_close.end_byte()
19951        || inner_close.end_byte() > outer_close.start_byte()
19952        || outer_close.end_byte() > semicolon.start_byte()
19953    {
19954        return None;
19955    }
19956    Some(PrototypeMacroCandidate {
19957        run_start: declaration.start_byte(),
19958        identifier_start: macro_name.start_byte(),
19959        inner_open_start: inner_open.start_byte(),
19960        inner_close_end: inner_close.end_byte(),
19961        outer_close_end: outer_close.end_byte(),
19962        semicolon_end: semicolon.end_byte(),
19963    })
19964}
19965
19966fn cpp_prototype_macro_candidate_from_pointer_expression(
19967    statement: Node<'_>,
19968    source: &str,
19969) -> Option<PrototypeMacroCandidate> {
19970    if statement.kind() != "expression_statement"
19971        || statement.named_child_count() != 1
19972        || !statement.has_error()
19973    {
19974        return None;
19975    }
19976    let expansion = statement
19977        .named_child(0)
19978        .filter(|child| child.kind() == "parameter_pack_expansion")?;
19979    let binary = expansion
19980        .child_by_field_name("pattern")
19981        .filter(|pattern| pattern.kind() == "binary_expression")?;
19982    if binary.child_count() != 3
19983        || binary
19984            .child(1)
19985            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19986        || expansion
19987            .child(expansion.child_count().checked_sub(1)?)
19988            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19989    {
19990        return None;
19991    }
19992    let return_type = binary.child_by_field_name("left")?;
19993    let call = binary
19994        .child_by_field_name("right")
19995        .filter(|right| right.kind() == "call_expression")?;
19996    let qualified = call.child_by_field_name("function")?;
19997    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19998    let arguments = call
19999        .child_by_field_name("arguments")
20000        .filter(|arguments| arguments.kind() == "argument_list")?;
20001    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20002    let semicolon = cpp_direct_semicolon(statement)?;
20003    if return_type.end_byte() > declared_name.start_byte()
20004        || macro_name.end_byte() > arguments.start_byte()
20005        || arguments.end_byte() > semicolon.start_byte()
20006    {
20007        return None;
20008    }
20009    Some(cpp_prototype_macro_candidate_from_parts(
20010        statement.start_byte(),
20011        macro_name,
20012        inner,
20013        arguments,
20014        semicolon,
20015    ))
20016}
20017
20018/// The inner parenthesized declarator of the `((parameters))` wrapper the
20019/// macro-declaration grammar makes of a prototype macro's argument list, with
20020/// the same span-keeping contract as [`cpp_prototype_macro_inner_arguments`]:
20021/// both paren pairs and their ownership come from the CST, and the returned
20022/// node's exact range is the one pair an ordinary function declarator needs.
20023fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
20024    if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
20025        return None;
20026    }
20027    let open = outer.child(0)?;
20028    let close = outer.child(outer.child_count().checked_sub(1)?)?;
20029    if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
20030        return None;
20031    }
20032    let inner = outer.named_child(0)?;
20033    if inner.kind() != "parenthesized_declarator" {
20034        return None;
20035    }
20036    let inner_open = inner.child(0)?;
20037    let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
20038    (inner_open.kind() == "("
20039        && !inner_open.is_missing()
20040        && inner_close.kind() == ")"
20041        && !inner_close.is_missing())
20042    .then_some(inner)
20043}
20044
20045/// Whether the macro-declaration grammar admitted a word of this declaration
20046/// as a `macro_modifier`, the structured proof that the declaration did not
20047/// parse without it.
20048fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
20049    let mut cursor = declaration.walk();
20050    declaration
20051        .named_children(&mut cursor)
20052        .any(|child| child.kind() == "macro_modifier")
20053}
20054
20055/// `RET name _((args));` as the macro-declaration grammar reads it when the
20056/// argument list stays an expression: the return-type word is admitted as a
20057/// `macro_modifier`, the declared name takes the `type` field, and the macro
20058/// token is the init declarator's callee. Same #2932 contract as the
20059/// ERROR-based readers above, on nodes that carry no `ERROR` of their own.
20060fn cpp_prototype_macro_candidate_from_macro_modifier_init(
20061    declaration: Node<'_>,
20062    source: &str,
20063) -> Option<PrototypeMacroCandidate> {
20064    if !cpp_declaration_has_macro_modifier(declaration) {
20065        return None;
20066    }
20067    let declared_name = declaration
20068        .child_by_field_name("type")
20069        .filter(|type_node| type_node.kind() == "type_identifier")?;
20070    let init = declaration
20071        .child_by_field_name("declarator")
20072        .filter(|declarator| declarator.kind() == "init_declarator")?;
20073    let macro_name = init
20074        .child_by_field_name("declarator")
20075        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20076    let arguments = init
20077        .child_by_field_name("value")
20078        .filter(|value| value.kind() == "argument_list")?;
20079    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20080    let semicolon = cpp_direct_semicolon(declaration)?;
20081    if declared_name.end_byte() > macro_name.start_byte()
20082        || macro_name.end_byte() > arguments.start_byte()
20083        || arguments.end_byte() > semicolon.start_byte()
20084    {
20085        return None;
20086    }
20087    Some(cpp_prototype_macro_candidate_from_parts(
20088        declaration.start_byte(),
20089        macro_name,
20090        inner,
20091        arguments,
20092        semicolon,
20093    ))
20094}
20095
20096/// `RET *name _((args));` as the macro-declaration grammar reads it when the
20097/// argument list stays declarator-shaped: the declared name and the macro
20098/// token prefix the nested parenthesized declarator as the modifiers of a
20099/// `macro_modifier_declarator` chain.
20100fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
20101    declaration: Node<'_>,
20102    source: &str,
20103) -> Option<PrototypeMacroCandidate> {
20104    let mut declarator = declaration.child_by_field_name("declarator")?;
20105    while declarator.kind() == "pointer_declarator" {
20106        declarator = declarator.child_by_field_name("declarator")?;
20107    }
20108    // The macro's own modifier must be preceded by the declared name's.
20109    let mut saw_name_modifier = false;
20110    let (macro_name, arguments) = loop {
20111        if declarator.kind() != "macro_modifier_declarator" {
20112            return None;
20113        }
20114        let mut cursor = declarator.walk();
20115        let modifier = declarator
20116            .named_children(&mut cursor)
20117            .find(|child| child.kind() == "macro_modifier")
20118            .and_then(macro_modifier_identifier)?;
20119        let inner = declarator.child_by_field_name("declarator")?;
20120        if cpp_is_prototype_macro_identifier(modifier, source) {
20121            break (modifier, inner);
20122        }
20123        saw_name_modifier = true;
20124        declarator = inner;
20125    };
20126    if !saw_name_modifier {
20127        return None;
20128    }
20129    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20130    let semicolon = cpp_direct_semicolon(declaration)?;
20131    Some(cpp_prototype_macro_candidate_from_parts(
20132        declaration.start_byte(),
20133        macro_name,
20134        inner,
20135        arguments,
20136        semicolon,
20137    ))
20138}
20139
20140/// `RET name _((args));` as the macro-declaration grammar reads it when the
20141/// macro token is the one word that could stand as the type: the return-type
20142/// words and the declared name are `macro_modifier`s and the nested argument
20143/// list is the whole declarator.
20144fn cpp_prototype_macro_candidate_from_macro_modifier_type(
20145    declaration: Node<'_>,
20146    source: &str,
20147) -> Option<PrototypeMacroCandidate> {
20148    if !cpp_declaration_has_macro_modifier(declaration) {
20149        return None;
20150    }
20151    let macro_name = declaration
20152        .child_by_field_name("type")
20153        .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
20154    let arguments = declaration.child_by_field_name("declarator")?;
20155    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20156    let semicolon = cpp_direct_semicolon(declaration)?;
20157    Some(cpp_prototype_macro_candidate_from_parts(
20158        declaration.start_byte(),
20159        macro_name,
20160        inner,
20161        arguments,
20162        semicolon,
20163    ))
20164}
20165
20166/// `RET name _((args));` as the macro-declaration grammar reads it when the
20167/// argument list itself needs recovery: the return-type word is a
20168/// `macro_modifier`, the declared name takes the `type` field, the macro token
20169/// is the first declarator, and the argument list's paren pairs land in the
20170/// bracketing `ERROR`s.
20171fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
20172    declaration: Node<'_>,
20173    source: &str,
20174) -> Option<PrototypeMacroCandidate> {
20175    if !cpp_declaration_has_macro_modifier(declaration) {
20176        return None;
20177    }
20178    let declared_name = declaration
20179        .child_by_field_name("type")
20180        .filter(|type_node| type_node.kind() == "type_identifier")?;
20181    let mut declarator_cursor = declaration.walk();
20182    let macro_name = declaration
20183        .children_by_field_name("declarator", &mut declarator_cursor)
20184        .next()
20185        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20186    let open_error = macro_name
20187        .next_named_sibling()
20188        .filter(|next| next.kind() == "ERROR")?;
20189    let close_error = last_named_child(declaration)
20190        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20191    if open_error.child_count() < 3
20192        || open_error
20193            .child(0)
20194            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20195        || open_error
20196            .child(1)
20197            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20198        || close_error.child_count() != 2
20199        || close_error
20200            .child(0)
20201            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20202        || close_error
20203            .child(1)
20204            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20205    {
20206        return None;
20207    }
20208    let inner_open = open_error.child(1)?;
20209    let inner_close = close_error.child(0)?;
20210    let outer_close = close_error.child(1)?;
20211    let semicolon = cpp_direct_semicolon(declaration)?;
20212    if declared_name.end_byte() > macro_name.start_byte()
20213        || macro_name.end_byte() > open_error.start_byte()
20214        || inner_open.start_byte() > inner_close.end_byte()
20215        || inner_close.end_byte() > outer_close.start_byte()
20216        || outer_close.end_byte() > semicolon.start_byte()
20217    {
20218        return None;
20219    }
20220    Some(PrototypeMacroCandidate {
20221        run_start: declaration.start_byte(),
20222        identifier_start: macro_name.start_byte(),
20223        inner_open_start: inner_open.start_byte(),
20224        inner_close_end: inner_close.end_byte(),
20225        outer_close_end: outer_close.end_byte(),
20226        semicolon_end: semicolon.end_byte(),
20227    })
20228}
20229
20230fn cpp_prototype_macro_candidate_from_parts(
20231    run_start: usize,
20232    macro_name: Node<'_>,
20233    inner_arguments: Node<'_>,
20234    outer_arguments: Node<'_>,
20235    semicolon: Node<'_>,
20236) -> PrototypeMacroCandidate {
20237    PrototypeMacroCandidate {
20238        run_start,
20239        identifier_start: macro_name.start_byte(),
20240        inner_open_start: inner_arguments.start_byte(),
20241        inner_close_end: inner_arguments.end_byte(),
20242        outer_close_end: outer_arguments.end_byte(),
20243        semicolon_end: semicolon.end_byte(),
20244    }
20245}
20246
20247/// Recover only CST shapes whose fields preserve the declaration name, macro
20248/// invocation, nested argument list, and terminator. Arbitrary flattened
20249/// `ERROR` wreckage stays unsupported rather than being interpreted through a
20250/// token or delimiter scan (issue #2932).
20251fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20252    let candidate = match node.kind() {
20253        "declaration" | "field_declaration" => {
20254            cpp_prototype_macro_candidate_from_init_declaration(node, source)
20255                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20256                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20257                .or_else(|| {
20258                    cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20259                })
20260                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20261                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20262        }
20263        "expression_statement" => {
20264            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20265        }
20266        _ => None,
20267    };
20268    candidate.into_iter().collect()
20269}
20270
20271fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20272    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20273        return false;
20274    }
20275    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20276        return false;
20277    }
20278    let Some(declarator) = (if node.kind() == "function_definition" {
20279        node.child_by_field_name("declarator")
20280            .and_then(extract_function_declarator)
20281    } else {
20282        node.named_child(0)
20283            .filter(|child| child.kind() == "function_declarator")
20284    }) else {
20285        return false;
20286    };
20287    let Some(name) = cpp_function_declarator_name_node(declarator) else {
20288        return false;
20289    };
20290    declarator.start_byte() == node.start_byte()
20291        && name.kind() == "identifier"
20292        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20293}
20294
20295/// Reparse a fragmented class-body interior while preserving its original byte
20296/// and line offsets, confined to the region by tree-sitter included ranges.
20297///
20298/// This used to materialize the region's whole file prefix as whitespace and
20299/// make the lexer walk it, the technique #1309 replaced on the other reparse
20300/// path: O(file) per fragmented-class recovery, on files that are already
20301/// error-recovered and already slow (#2788). Included ranges give the parser
20302/// the same view -- the region's bytes, at their original offsets and
20303/// line/column positions -- without materializing or lexing anything before it.
20304///
20305/// The equality of the two views is the claim, so a debug build parses both and
20306/// asserts the trees agree node for node.
20307fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20308    let region = cpp_reparse_region_items(source, start, end);
20309
20310    #[cfg(debug_assertions)]
20311    assert_eq!(
20312        region.as_ref().map(cpp_tree_shape),
20313        cpp_reparse_padded_class_body(source, start, end)
20314            .as_ref()
20315            .map(cpp_tree_shape),
20316        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20317         prefix produces"
20318    );
20319
20320    region
20321}
20322
20323/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
20324/// replaces, kept as the oracle a debug build asserts every region reparse
20325/// against and as the release-mode parity tests' reference (#2788).
20326#[cfg(any(debug_assertions, test))]
20327fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20328    if start >= end {
20329        // The included-range parser refuses an empty region; the padded one
20330        // returned a tree holding nothing, which every caller read as "no
20331        // members here".
20332        return None;
20333    }
20334    let bytes = source.as_bytes();
20335    let prefix = bytes.get(..start)?;
20336    let interior = bytes.get(start..end)?;
20337    let mut padded = Vec::with_capacity(end);
20338    padded.extend(
20339        prefix
20340            .iter()
20341            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20342    );
20343    padded.extend_from_slice(interior);
20344    let padded = String::from_utf8(padded).ok()?;
20345    let mut parser = tree_sitter::Parser::new();
20346    parser
20347        .set_language(&tree_sitter_cpp::LANGUAGE.into())
20348        .ok()?;
20349    parser.parse(&padded, None)
20350}
20351
20352/// Every node of `tree` in preorder, by kind, span and position, which is what
20353/// two reparses of one region have to agree on for their callers to read the
20354/// same declarations out of either (#2788).
20355#[cfg(any(debug_assertions, test))]
20356fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20357    let mut shape = Vec::new();
20358    let mut cursor = tree.root_node().walk();
20359    let mut stack = vec![tree.root_node()];
20360    while let Some(node) = stack.pop() {
20361        shape.push((
20362            node.kind(),
20363            node.start_byte(),
20364            node.end_byte(),
20365            node.start_position().row,
20366            node.start_position().column,
20367            node.is_named(),
20368            node.is_missing(),
20369        ));
20370        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20371        stack.extend(children.into_iter().rev());
20372    }
20373    shape
20374}
20375
20376/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
20377/// reparsed interior is indexed only when every top-level named node is a
20378/// well-formed C++ item (or a comment) and at least one real item is present.
20379/// Expression/statement soup surfaces as a top-level `ERROR` or
20380/// `expression_statement`, neither of which is an item kind, so it is rejected.
20381///
20382/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
20383/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
20384/// swallowed by a preceding dangling sentinel) reparses to a real
20385/// `namespace_definition` whose body still holds a bogus `function_definition`,
20386/// so the subtree legitimately carries an error. Container items are admitted
20387/// even with an internal error; the inner bogus function is recovered recursively
20388/// when `visit_function_definition` walks it. Each recursion strips at least one
20389/// leading sentinel, so the region strictly shrinks and recovery terminates.
20390///
20391/// A top-level `function_definition` is the one place we stay strict: it is
20392/// admitted only when it is clean or is itself a sentinel candidate. A function
20393/// that has an error and is not a sentinel is a real callable with a broken body,
20394/// so we refuse the whole reparse and let the ordinary path handle it (preserving
20395/// its real return type rather than re-deriving an implicit one).
20396fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20397    let mut cursor = root.walk();
20398    let mut saw_item = false;
20399    for child in root.named_children(&mut cursor) {
20400        match child.kind() {
20401            "comment" => {}
20402            "function_definition" => {
20403                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20404                    return false;
20405                }
20406                saw_item = true;
20407            }
20408            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20409            _ => return false,
20410        }
20411    }
20412    saw_item
20413}
20414
20415/// Robustness gate for a reparsed fragmented multiple-base export class body
20416/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
20417/// kinds a class body produces when reparsed at translation-unit scope: the
20418/// access-specifier label preceding the first member surfaces as a
20419/// `labeled_statement` wrapping that member, and members surface as
20420/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
20421/// Statement or expression soup surfaces as other top-level kinds and is rejected,
20422/// so only a genuinely member-shaped body is ever re-owned as members; anything
20423/// ambiguous falls back to indexing the class alone.
20424fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20425    if node.kind() != "ERROR" {
20426        return false;
20427    }
20428    let mut stack = Vec::new();
20429    let mut saw_function_declarator = false;
20430    let mut cursor = node.walk();
20431    for child in node.named_children(&mut cursor) {
20432        stack.push(child);
20433    }
20434    while let Some(current) = stack.pop() {
20435        match current.kind() {
20436            // Tree-sitter may wrap adjacent copy-control declarations in a
20437            // nested ERROR. Keep descending only through ERROR wrappers; the
20438            // actual declaration payload must be a function_declarator.
20439            "ERROR" => {
20440                let mut cursor = current.walk();
20441                stack.extend(current.named_children(&mut cursor));
20442            }
20443            "function_declarator" => saw_function_declarator = true,
20444            _ => return false,
20445        }
20446    }
20447    saw_function_declarator
20448}
20449
20450fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20451    if node.kind() != "ERROR" {
20452        return false;
20453    }
20454    let mut cursor = node.walk();
20455    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20456    let [explicit, constructor_error, tail] = named.as_slice() else {
20457        return false;
20458    };
20459    if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20460        return false;
20461    }
20462    // The copy-control declarators the inner ERROR holds, with only their `;`
20463    // tokens beside them. The older grammar kept the constructor alone there
20464    // and gave the destructor the slot after it; the macro-declaration grammar
20465    // absorbs the destructor beside the constructor and folds the member
20466    // written after them into the tail slot as a `macro_modifier_declarator`.
20467    let mut declarators = Vec::new();
20468    let mut stack = vec![*constructor_error];
20469    while let Some(current) = stack.pop() {
20470        match current.kind() {
20471            "ERROR" => {
20472                let mut cursor = current.walk();
20473                let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20474                stack.extend(children.into_iter().rev());
20475            }
20476            "function_declarator" => declarators.push(current),
20477            _ => return false,
20478        }
20479    }
20480    let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20481        ([constructor], "function_declarator") => (*constructor, *tail),
20482        ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20483        _ => return false,
20484    };
20485    let Some(constructor_name) =
20486        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20487    else {
20488        return false;
20489    };
20490    let Some(destructor_name) =
20491        extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20492    else {
20493        return false;
20494    };
20495    let Some(destroyed_type) = destructor_name.named_child(0) else {
20496        return false;
20497    };
20498    constructor_name.kind() == "identifier"
20499        && destructor_name.kind() == "destructor_name"
20500        && destroyed_type.kind() == "identifier"
20501        && node_text(constructor_name, source) == node_text(destroyed_type, source)
20502}
20503
20504fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20505    if node.kind() != "compound_statement" {
20506        return false;
20507    }
20508    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20509        return false;
20510    };
20511    if prefix.kind() == "labeled_statement"
20512        && prefix.named_child(0).is_some_and(|label| {
20513            matches!(
20514                node_text(label, source).trim(),
20515                "public" | "private" | "protected"
20516            )
20517        })
20518    {
20519        return prefix.named_children(&mut prefix.walk()).any(|child| {
20520            child.kind() == "declaration"
20521                && child.has_error()
20522                && child
20523                    .named_children(&mut child.walk())
20524                    .any(cpp_reparsed_member_error_is_indexable)
20525        });
20526    }
20527    // A malformed constructor initializer can be split into a declaration
20528    // followed by its compound body when the class prefix already contains
20529    // realistic members. Keep this admission tied to that exact structured
20530    // declaration/error/body chain rather than accepting arbitrary blocks.
20531    prefix.kind() == "declaration"
20532        && prefix.has_error()
20533        && prefix
20534            .named_children(&mut prefix.walk())
20535            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20536}
20537
20538fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20539    if !cpp_reparsed_member_error_is_indexable(node) {
20540        return false;
20541    }
20542    let Some(preproc) = node.next_named_sibling() else {
20543        return false;
20544    };
20545    preproc.kind() == "preproc_if"
20546        && preproc.has_error()
20547        && preproc
20548            .named_children(&mut preproc.walk())
20549            .any(|child| child.kind() == "expression_statement" && child.has_error())
20550        && preproc
20551            .next_named_sibling()
20552            .is_some_and(|body| body.kind() == "compound_statement")
20553}
20554
20555/// Return a function body whose braces and ownership are explicit in the
20556/// reparsed class-member tree. An error below a real function envelope is
20557/// recoverable by the ordinary function visitor; a missing/deferred body is
20558/// not, because accepting it would let statement soup masquerade as a member.
20559fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20560    if node.kind() != "function_definition" {
20561        return None;
20562    }
20563    let body = node.child_by_field_name("body")?;
20564    if body.kind() != "compound_statement" {
20565        return None;
20566    }
20567    let open = body.child(0)?;
20568    let close = body.child(body.child_count().checked_sub(1)?)?;
20569    if open.kind() != "{"
20570        || open.is_missing()
20571        || close.kind() != "}"
20572        || close.is_missing()
20573        || close.end_byte() != body.end_byte()
20574        || body.end_byte() != node.end_byte()
20575    {
20576        return None;
20577    }
20578    Some(body)
20579}
20580
20581fn cpp_reparsed_member_function_errors_are_in_body(
20582    node: Node<'_>,
20583    body: Node<'_>,
20584    source: &str,
20585) -> bool {
20586    let mut cursor = node.walk();
20587    node.children(&mut cursor).all(|child| {
20588        same_node(child, body)
20589            || cpp_reparsed_member_attribute_error(child, source)
20590            || cpp_reparsed_member_signature_identifier_errors(child)
20591            || (!child.has_error() && !child.is_error() && !child.is_missing())
20592    })
20593}
20594
20595/// A complete callable can still carry parser errors in its signature when a
20596/// project annotation is not part of the C++ grammar (`nonneg int`,
20597/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
20598/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
20599/// leaves inside the already-proven callable envelope; structured statements,
20600/// literals, missing tokens, and other malformed signature payload remain
20601/// rejected.
20602fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20603    if !node.has_error() && !node.is_error() && !node.is_missing() {
20604        return false;
20605    }
20606    let mut stack = vec![node];
20607    let mut saw_error = false;
20608    while let Some(current) = stack.pop() {
20609        if current.is_missing() {
20610            return false;
20611        }
20612        if current.kind() == "ERROR" {
20613            saw_error = true;
20614            let mut cursor = current.walk();
20615            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20616            if children
20617                .iter()
20618                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20619            {
20620                return false;
20621            }
20622            stack.extend(children);
20623            continue;
20624        }
20625        let mut cursor = current.walk();
20626        stack.extend(current.children(&mut cursor));
20627    }
20628    saw_error
20629}
20630
20631fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20632    node.kind() == "ERROR"
20633        && node.named_child_count() == 1
20634        && node
20635            .named_child(0)
20636            .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20637}
20638
20639fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20640    node.kind() == "identifier"
20641        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20642}
20643
20644/// A C++ attribute placed between a member's declarator and body can make
20645/// tree-sitter expose the callable as
20646/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
20647/// Keep this admission tied to that exact node geometry. In particular, an
20648/// arbitrary ERROR or identifier before a compound statement is not enough.
20649fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20650    let Some(body) = cpp_reparsed_member_function_body(node) else {
20651        return false;
20652    };
20653    let mut cursor = node.walk();
20654    let named = node
20655        .named_children(&mut cursor)
20656        .filter(|child| child.kind() != "comment")
20657        .collect::<Vec<_>>();
20658    let [type_node, error, attribute, body_node] = named.as_slice() else {
20659        return false;
20660    };
20661    if !same_node(*body_node, body)
20662        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20663        || !cpp_reparsed_export_macro_identifier(*attribute, source)
20664        || error.kind() != "ERROR"
20665        || error.named_child_count() != 1
20666    {
20667        return false;
20668    }
20669    error
20670        .named_child(0)
20671        .is_some_and(cpp_reparsed_attribute_callable_declarator)
20672}
20673
20674fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20675    cpp_structured_type_path(node, source).is_some()
20676        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20677}
20678
20679fn cpp_reparsed_member_return_and_declarator_are_indexable(
20680    return_error: Node<'_>,
20681    declarator: Node<'_>,
20682    body_node: Node<'_>,
20683    body: Node<'_>,
20684    source: &str,
20685) -> bool {
20686    return_error.kind() == "ERROR"
20687        && return_error.named_child_count() == 1
20688        && same_node(body_node, body)
20689        && return_error.named_child(0).is_some_and(|return_type| {
20690            cpp_reparsed_member_return_type_is_indexable(return_type, source)
20691        })
20692        && extract_function_declarator(declarator)
20693            .and_then(cpp_function_declarator_name_node)
20694            .is_some()
20695}
20696
20697fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20698    let Some(body) = cpp_reparsed_member_function_body(node) else {
20699        return false;
20700    };
20701    let mut cursor = node.walk();
20702    let named = node
20703        .named_children(&mut cursor)
20704        .filter(|child| child.kind() != "comment")
20705        .collect::<Vec<_>>();
20706    let [friend, return_error, declarator, body_node] = named.as_slice() else {
20707        return false;
20708    };
20709    friend.kind() == "type_identifier"
20710        && node_text(*friend, source) == "friend"
20711        && cpp_reparsed_member_return_and_declarator_are_indexable(
20712            *return_error,
20713            *declarator,
20714            *body_node,
20715            body,
20716            source,
20717        )
20718}
20719
20720fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20721    let Some(body) = cpp_reparsed_member_function_body(node) else {
20722        return false;
20723    };
20724    let mut cursor = node.walk();
20725    let named = node
20726        .named_children(&mut cursor)
20727        .filter(|child| child.kind() != "comment")
20728        .collect::<Vec<_>>();
20729    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20730        return false;
20731    };
20732    prefix
20733        .iter()
20734        .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20735        && attribute.kind() == "type_identifier"
20736        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20737        && cpp_reparsed_member_return_and_declarator_are_indexable(
20738            *return_error,
20739            *declarator,
20740            *body_node,
20741            body,
20742            source,
20743        )
20744}
20745
20746/// An included-range reparse that begins inside a malformed class can merge an
20747/// access label and following template member. Tree-sitter then emits the label
20748/// as the `template_type` name, the template parameter list as its arguments,
20749/// an ERROR-wrapped return type, the callable declarator, and its complete
20750/// body. Admit only that exact structured displacement.
20751fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20752    let Some(body) = cpp_reparsed_member_function_body(node) else {
20753        return false;
20754    };
20755    let mut cursor = node.walk();
20756    let named = node
20757        .named_children(&mut cursor)
20758        .filter(|child| child.kind() != "comment")
20759        .collect::<Vec<_>>();
20760    // The member's return type is an `ERROR` of its own, or -- under the
20761    // macro-declaration grammar, which admits it as a modifier of the
20762    // declarator it could not separate -- folded into the declarator's
20763    // `macro_modifier_declarator` wrapper.
20764    let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20765        [template_type, return_error, declarator, body_node] => {
20766            let Some(return_type) = return_error.named_child(0) else {
20767                return false;
20768            };
20769            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20770                return false;
20771            }
20772            (*template_type, return_type, *declarator, *body_node)
20773        }
20774        [template_type, declarator, body_node] => {
20775            let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20776                .then(|| {
20777                    let mut cursor = declarator.walk();
20778                    declarator
20779                        .named_children(&mut cursor)
20780                        .find(|child| child.kind() == "macro_modifier")
20781                })
20782                .flatten()
20783                .and_then(macro_modifier_identifier)
20784            else {
20785                return false;
20786            };
20787            let Some(function) = declarator.child_by_field_name("declarator") else {
20788                return false;
20789            };
20790            (*template_type, return_type, function, *body_node)
20791        }
20792        _ => return false,
20793    };
20794    let Some(template_name) = template_type.child_by_field_name("name") else {
20795        return false;
20796    };
20797    let Some(arguments) = template_type.child_by_field_name("arguments") else {
20798        return false;
20799    };
20800    let mut cursor = template_type.walk();
20801    let template_errors = template_type
20802        .named_children(&mut cursor)
20803        .filter(|child| child.kind() == "ERROR")
20804        .collect::<Vec<_>>();
20805    let [comment_error] = template_errors.as_slice() else {
20806        return false;
20807    };
20808    let mut cursor = comment_error.walk();
20809    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20810    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20811        return false;
20812    };
20813    same_node(body_node, body)
20814        && template_type.kind() == "template_type"
20815        && template_name.kind() == "type_identifier"
20816        && matches!(
20817            node_text(template_name, source).trim(),
20818            "public" | "private" | "protected"
20819        )
20820        && arguments.kind() == "template_argument_list"
20821        && arguments.named_child_count() > 0
20822        && !arguments.has_error()
20823        && !colon.is_named()
20824        && colon.kind() == ":"
20825        && comments.iter().all(|child| child.kind() == "comment")
20826        && !template_keyword.is_named()
20827        && template_keyword.kind() == "template"
20828        && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20829        && extract_function_declarator(declarator)
20830            .and_then(cpp_function_declarator_name_node)
20831            .is_some()
20832}
20833
20834/// Return the constructor declaration tree-sitter can merge into an access
20835/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
20836/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
20837/// declaration's apparent type; the callable name must still exactly match the
20838/// recovered class, so unrelated labeled statements are never re-owned.
20839fn cpp_reparsed_preprocessor_constructor<'tree>(
20840    node: Node<'tree>,
20841    class_name: &str,
20842    source: &str,
20843) -> Option<Node<'tree>> {
20844    if node.kind() != "labeled_statement" {
20845        return None;
20846    }
20847    let mut cursor = node.walk();
20848    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20849    let [label, directive_error, declaration] = named.as_slice() else {
20850        return None;
20851    };
20852    if label.kind() != "statement_identifier"
20853        || !matches!(
20854            node_text(*label, source),
20855            "public" | "private" | "protected"
20856        )
20857        || directive_error.kind() != "ERROR"
20858        || directive_error.child_count() != 1
20859        || directive_error
20860            .child(0)
20861            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20862        || declaration.kind() != "declaration"
20863        || declaration.named_child_count() != 2
20864    {
20865        return None;
20866    }
20867    let apparent_type = declaration.child_by_field_name("type")?;
20868    if apparent_type.kind() != "type_identifier"
20869        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20870    {
20871        return None;
20872    }
20873    let declarator = declaration.child_by_field_name("declarator")?;
20874    let function = extract_function_declarator(declarator)?;
20875    let name = cpp_function_declarator_name_node(function)?;
20876    (node_text(name, source) == class_name).then_some(*declaration)
20877}
20878
20879fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20880    if extract_function_declarator(node)
20881        .and_then(cpp_function_declarator_name_node)
20882        .is_some()
20883    {
20884        return true;
20885    }
20886    node.kind() == "init_declarator"
20887        && node
20888            .child_by_field_name("declarator")
20889            .is_some_and(|declarator| declarator.kind() == "identifier")
20890        && node
20891            .child_by_field_name("value")
20892            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20893}
20894
20895/// Return true for the constrained/attribute form that tree-sitter splits into
20896/// an ERROR declaration, a preprocessor `requires` clause, and a following
20897/// compound statement. The three nodes must remain immediate named siblings;
20898/// this deliberately does not search source text or skip unrelated statements.
20899fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20900    if node.kind() != "ERROR" || node.named_child_count() != 3 {
20901        return false;
20902    }
20903    let mut cursor = node.walk();
20904    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20905    let [type_node, function_declarator, attribute] = named.as_slice() else {
20906        return false;
20907    };
20908    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20909        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20910        || attribute.kind() != "identifier"
20911        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20912    {
20913        return false;
20914    }
20915    let Some(preproc) =
20916        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20917    else {
20918        return false;
20919    };
20920    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20921        .filter(|sibling| sibling.kind() == "compound_statement")
20922    else {
20923        return false;
20924    };
20925    let Some(open) = body.child(0) else {
20926        return false;
20927    };
20928    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20929        return false;
20930    };
20931    let Some(condition) = preproc.child_by_field_name("condition") else {
20932        return false;
20933    };
20934    let mut cursor = preproc.walk();
20935    let payload = preproc
20936        .named_children(&mut cursor)
20937        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20938        .collect::<Vec<_>>();
20939    let [requires_statement] = payload.as_slice() else {
20940        return false;
20941    };
20942    let requires_clause = requires_statement.named_child(0);
20943
20944    open.kind() == "{"
20945        && !open.is_missing()
20946        && close.kind() == "}"
20947        && !close.is_missing()
20948        && close.end_byte() == body.end_byte()
20949        && requires_statement.kind() == "expression_statement"
20950        && requires_statement.named_child_count() == 1
20951        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20952}
20953
20954fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20955    let mut sibling = node.next_named_sibling();
20956    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20957        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20958    }
20959    sibling
20960}
20961
20962fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20963    let mut sibling = node.prev_named_sibling();
20964    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20965        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20966    }
20967    sibling
20968}
20969
20970fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20971    let Some(preproc) =
20972        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20973    else {
20974        return false;
20975    };
20976    let Some(error) =
20977        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20978    else {
20979        return false;
20980    };
20981    cpp_reparsed_attribute_requires_error(error, source)
20982}
20983
20984fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20985    parameter: Node<'tree>,
20986    macro_name: Node<'tree>,
20987    message: Node<'tree>,
20988    source: &str,
20989) -> Option<Node<'tree>> {
20990    let parameter_name = parameter.named_child(0)?;
20991    (parameter.kind() == "type_parameter_declaration"
20992        && parameter_name.kind() == "type_identifier"
20993        && matches!(macro_name.kind(), "identifier" | "type_identifier")
20994        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20995        && message.kind() == "string_literal")
20996        .then_some(parameter_name)
20997}
20998
20999fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
21000    node: Node<'tree>,
21001    source: &str,
21002) -> Option<Node<'tree>> {
21003    if node.kind() != "ERROR" {
21004        return None;
21005    }
21006    let mut cursor = node.walk();
21007    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21008    let [parameter, macro_name, message] = named.as_slice() else {
21009        return None;
21010    };
21011    cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
21012}
21013
21014/// Recognize the alternate constraint-macro prefix where tree-sitter retains
21015/// the complete qualified constraint as a fourth child instead of moving it
21016/// into the following function. Keep the gate tied to a two-type template
21017/// constraint that names the declared type parameter.
21018fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
21019    node: Node<'tree>,
21020    source: &str,
21021) -> Option<Node<'tree>> {
21022    if node.kind() != "ERROR" {
21023        return None;
21024    }
21025    let mut cursor = node.walk();
21026    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21027    let [parameter, macro_name, message, constraint] = named.as_slice() else {
21028        return None;
21029    };
21030    let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
21031        *parameter,
21032        *macro_name,
21033        *message,
21034        source,
21035    )?;
21036    let constraint_scope = constraint.child_by_field_name("scope")?;
21037    let constraint_template = constraint.child_by_field_name("name")?;
21038    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21039    let mut argument_cursor = constraint_arguments.walk();
21040    let constraint_types = constraint_arguments
21041        .named_children(&mut argument_cursor)
21042        .collect::<Vec<_>>();
21043    if constraint.kind() != "qualified_identifier"
21044        || constraint_scope.kind() != "namespace_identifier"
21045        || !matches!(
21046            constraint_template.kind(),
21047            "template_function" | "template_type"
21048        )
21049        || !matches!(constraint_types.as_slice(), [left, right]
21050            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21051        || constraint_arguments.has_error()
21052    {
21053        return None;
21054    }
21055    let parameter_text = node_text(parameter_name, source);
21056    let mut stack = constraint_types;
21057    while let Some(current) = stack.pop() {
21058        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
21059            return Some(parameter_name);
21060        }
21061        let mut cursor = current.walk();
21062        stack.extend(current.named_children(&mut cursor));
21063    }
21064    None
21065}
21066
21067fn cpp_reparsed_template_macro_companion_is_indexable(
21068    node: Node<'_>,
21069    parameter_name: Node<'_>,
21070    source: &str,
21071) -> bool {
21072    let Some(body) = cpp_reparsed_member_function_body(node) else {
21073        return false;
21074    };
21075    let mut cursor = node.walk();
21076    let named = node
21077        .named_children(&mut cursor)
21078        .filter(|child| child.kind() != "comment")
21079        .collect::<Vec<_>>();
21080    // The member's return type is an `ERROR` of its own, or -- under the
21081    // macro-declaration grammar, which admits it as a modifier of the
21082    // declarator it could not separate -- folded into the declarator's
21083    // `macro_modifier_declarator` wrapper.
21084    let (constraint, close_error, storage, return_type, declarator, body_node) = match named
21085        .as_slice()
21086    {
21087        [
21088            constraint,
21089            close_error,
21090            storage,
21091            return_error,
21092            declarator,
21093            body_node,
21094        ] => {
21095            let Some(return_type) = return_error.named_child(0) else {
21096                return false;
21097            };
21098            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21099                return false;
21100            }
21101            (
21102                *constraint,
21103                *close_error,
21104                *storage,
21105                return_type,
21106                *declarator,
21107                *body_node,
21108            )
21109        }
21110        [constraint, close_error, storage, declarator, body_node] => {
21111            let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
21112                return false;
21113            };
21114            (
21115                *constraint,
21116                *close_error,
21117                *storage,
21118                return_type,
21119                function,
21120                *body_node,
21121            )
21122        }
21123        _ => return false,
21124    };
21125    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
21126        return false;
21127    };
21128    let Some(constraint_template) = constraint.child_by_field_name("name") else {
21129        return false;
21130    };
21131    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
21132        return false;
21133    };
21134    let mut cursor = constraint_arguments.walk();
21135    let constraint_types = constraint_arguments
21136        .named_children(&mut cursor)
21137        .collect::<Vec<_>>();
21138    same_node(body_node, body)
21139        && constraint.kind() == "qualified_identifier"
21140        && constraint_scope.kind() == "namespace_identifier"
21141        && constraint_template.kind() == "template_type"
21142        && matches!(constraint_types.as_slice(), [left, right]
21143            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21144        && !constraint_arguments.has_error()
21145        && close_error.kind() == "ERROR"
21146        && close_error.named_child_count() == 0
21147        && storage.kind() == "storage_class_specifier"
21148        && return_type.kind() == "identifier"
21149        && node_text(return_type, source) == node_text(parameter_name, source)
21150        && extract_function_declarator(declarator)
21151            .and_then(cpp_function_declarator_name_node)
21152            .is_some()
21153}
21154
21155fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
21156    node: Node<'tree>,
21157    parameter_name: Node<'_>,
21158    source: &str,
21159) -> Option<Node<'tree>> {
21160    let body = cpp_reparsed_member_function_body(node)?;
21161    let constraint = node.child_by_field_name("type")?;
21162    let constraint_template = constraint.child_by_field_name("name")?;
21163    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21164    let mut argument_cursor = constraint_arguments.walk();
21165    let constraint_types = constraint_arguments
21166        .named_children(&mut argument_cursor)
21167        .collect::<Vec<_>>();
21168    if constraint.kind() != "qualified_identifier"
21169        || constraint_template.kind() != "template_type"
21170        || !matches!(constraint_types.as_slice(), [left, right]
21171            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21172        || constraint_arguments.has_error()
21173        || node
21174            .child_by_field_name("body")
21175            .is_none_or(|candidate| !same_node(candidate, body))
21176    {
21177        return None;
21178    }
21179
21180    let mut cursor = node.walk();
21181    let recovery_errors = node
21182        .named_children(&mut cursor)
21183        .filter(|child| child.kind() == "ERROR")
21184        .collect::<Vec<_>>();
21185    if !recovery_errors
21186        .iter()
21187        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21188        || !recovery_errors.iter().all(|error| {
21189            error.named_child_count() == 0
21190                || cpp_reparsed_constraint_macro_error(*error, source)
21191                || (error.named_child_count() == 1
21192                    && error
21193                        .named_child(0)
21194                        .is_some_and(|child| child.kind() == "function_declarator"))
21195        })
21196    {
21197        return None;
21198    }
21199
21200    let parameter_text = node_text(parameter_name, source);
21201    let mut declarators = node
21202        .child_by_field_name("declarator")
21203        .and_then(extract_function_declarator)
21204        .into_iter()
21205        .collect::<Vec<_>>();
21206    for error in recovery_errors {
21207        let mut stack = vec![error];
21208        while let Some(current) = stack.pop() {
21209            if current.kind() == "function_declarator" {
21210                declarators.push(current);
21211            }
21212            let mut cursor = current.walk();
21213            stack.extend(current.named_children(&mut cursor));
21214        }
21215    }
21216    declarators.into_iter().find(|declarator| {
21217        cpp_function_declarator_name_node(*declarator)
21218            .is_some_and(|name| name.kind() == "identifier")
21219            && declarator
21220                .child_by_field_name("parameters")
21221                .is_some_and(|parameters| {
21222                    parameters
21223                        .named_children(&mut parameters.walk())
21224                        .filter_map(|parameter| parameter.child_by_field_name("type"))
21225                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21226                })
21227    })
21228}
21229
21230fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21231    node: Node<'_>,
21232    parameter_name: Node<'_>,
21233    source: &str,
21234) -> bool {
21235    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21236}
21237
21238fn cpp_reparsed_template_macro_function_companion_is_indexable(
21239    node: Node<'_>,
21240    parameter_name: Node<'_>,
21241    source: &str,
21242) -> bool {
21243    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21244        return false;
21245    }
21246    let Some(return_type) = node.child_by_field_name("type") else {
21247        return false;
21248    };
21249    let Some(function_declarator) = node
21250        .child_by_field_name("declarator")
21251        .and_then(extract_function_declarator)
21252    else {
21253        return false;
21254    };
21255    if cpp_function_declarator_name_node(function_declarator).is_none()
21256        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21257    {
21258        return false;
21259    }
21260    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21261        return false;
21262    };
21263    let parameter_text = node_text(parameter_name, source);
21264    parameters
21265        .named_children(&mut parameters.walk())
21266        .any(|parameter| {
21267            parameter
21268                .child_by_field_name("type")
21269                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21270        })
21271}
21272
21273fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21274    if node.kind() != "ERROR" {
21275        return false;
21276    }
21277    let mut stack = vec![node];
21278    while let Some(current) = stack.pop() {
21279        let macro_shape = match current.kind() {
21280            "call_expression" => current
21281                .child_by_field_name("function")
21282                .zip(current.child_by_field_name("arguments")),
21283            "init_declarator" => current
21284                .child_by_field_name("declarator")
21285                .zip(current.child_by_field_name("value")),
21286            _ => None,
21287        };
21288        if let Some((name, arguments)) = macro_shape
21289            && name.kind() == "identifier"
21290            && arguments.kind() == "argument_list"
21291            && arguments.named_child_count() >= 2
21292            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21293        {
21294            return true;
21295        }
21296        let mut cursor = current.walk();
21297        stack.extend(current.named_children(&mut cursor));
21298    }
21299    false
21300}
21301
21302fn cpp_recovered_template_macro_constructor<'tree>(
21303    node: Node<'tree>,
21304    source: &str,
21305) -> Option<(Node<'tree>, Node<'tree>)> {
21306    let mut prefix = node.prev_named_sibling()?;
21307    while prefix.kind() == "comment" {
21308        prefix = prefix.prev_named_sibling()?;
21309    }
21310    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21311    let parameter = parameter_name
21312        .parent()
21313        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21314    let declarator =
21315        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21316    Some((declarator, parameter))
21317}
21318
21319/// The template parameter a split constraint-macro head declares for the
21320/// member function after it, when the recovery gate admits the pair.
21321/// `template <class U, REQUIRES("...", std::is_convertible<U, int>)>` leaves
21322/// the head as an `ERROR` beside a clean function definition; the parameter is
21323/// the head's structured `type_parameter_declaration`.
21324fn cpp_recovered_template_macro_function_parameter<'tree>(
21325    node: Node<'tree>,
21326    source: &str,
21327) -> Option<Node<'tree>> {
21328    let prefix = cpp_prev_non_comment_named_sibling(node)?;
21329    let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21330    if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21331        return None;
21332    }
21333    parameter_name
21334        .parent()
21335        .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21336}
21337
21338fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21339    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21340        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21341            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21342                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21343                    function,
21344                    parameter_name,
21345                    source,
21346                )
21347        });
21348    }
21349    let Some(parameter_name) =
21350        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21351    else {
21352        return false;
21353    };
21354    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21355        cpp_reparsed_template_macro_function_companion_is_indexable(
21356            function,
21357            parameter_name,
21358            source,
21359        )
21360    })
21361}
21362
21363fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21364    let function_name = node
21365        .child_by_field_name("declarator")
21366        .and_then(extract_function_declarator)
21367        .and_then(cpp_function_declarator_name_node);
21368    if let Some(body) = cpp_reparsed_member_function_body(node)
21369        && function_name.is_some()
21370        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21371    {
21372        return true;
21373    }
21374    cpp_reparsed_attribute_member_function(node, source)
21375        || cpp_reparsed_friend_function_is_indexable(node, source)
21376        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21377        || cpp_reparsed_access_template_function_is_indexable(node, source)
21378        || cpp_recovered_template_macro_constructor(node, source).is_some()
21379}
21380
21381/// Recognize the three top-level nodes produced when an unknown attribute
21382/// macro separates an inline member's declarator from its body in a reparsed
21383/// class interior: an errorful declaration with a missing semicolon, the macro
21384/// call expression, and the complete compound body. Their adjacency and exact
21385/// structured shapes prove one recoverable member envelope; arbitrary calls or
21386/// blocks do not pass this gate.
21387fn cpp_reparsed_macro_attribute_member_sequence(
21388    children: &[Node<'_>],
21389    index: usize,
21390    source: &str,
21391) -> bool {
21392    let Some(prefix) = children.get(index).copied() else {
21393        return false;
21394    };
21395    let declaration = if prefix.kind() == "labeled_statement" {
21396        prefix
21397            .named_child(prefix.named_child_count().saturating_sub(1))
21398            .filter(|child| child.kind() == "declaration")
21399    } else {
21400        (prefix.kind() == "declaration").then_some(prefix)
21401    };
21402    let Some(declaration) = declaration else {
21403        return false;
21404    };
21405    if !declaration.has_error()
21406        || declaration
21407            .child_by_field_name("declarator")
21408            .and_then(extract_function_declarator)
21409            .and_then(cpp_function_declarator_name_node)
21410            .is_none()
21411    {
21412        return false;
21413    }
21414    let Some(attribute_statement) = children.get(index + 1).copied() else {
21415        return false;
21416    };
21417    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21418        .then(|| attribute_statement.named_child(0))
21419        .flatten()
21420        .filter(|child| child.kind() == "call_expression")
21421    else {
21422        return false;
21423    };
21424    let Some(attribute_name) = attribute_call
21425        .child_by_field_name("function")
21426        .filter(|function| function.kind() == "identifier")
21427        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21428    else {
21429        return false;
21430    };
21431    if !cpp_export_macro_token(&attribute_name) {
21432        return false;
21433    }
21434    let Some(body) = children.get(index + 2).copied() else {
21435        return false;
21436    };
21437    body.kind() == "compound_statement"
21438        && body.child(0).is_some_and(|open| open.kind() == "{")
21439        && body
21440            .child(body.child_count().saturating_sub(1))
21441            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21442        && declaration.end_byte() <= attribute_statement.start_byte()
21443        && attribute_statement.end_byte() <= body.start_byte()
21444}
21445
21446/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
21447/// types and specifiers followed by a declarator, over and over, with nothing
21448/// left over. A string-argument attribute macro is what strands them there, and
21449/// the walk indexes exactly what this reads, so admitting the region is safe
21450/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
21451/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
21452/// the whole class body.
21453fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21454    if node.kind() != "ERROR" {
21455        return false;
21456    }
21457    let run = stranded_declaration_run(node, source);
21458    run.complete && !run.declarations.is_empty()
21459}
21460
21461/// The ABI 15 grammar reads a defaulted zero-argument constructor in a
21462/// translation-unit-shaped class-body reparse as an assignment expression:
21463/// `Ctor() = default`. Admit only the exact assignment shape, and only when
21464/// the same reparse retains a separate constructor declarator for that name.
21465/// The second condition keeps an arbitrary expression statement from becoming
21466/// a member merely because it contains a callee followed by `default`.
21467/// A constructor declaration a statement-context class-body reparse reads as
21468/// a bare call statement (`value(pn_data_t* d);`). At statement scope a
21469/// constructor declaration is indistinguishable from a call, but a direct
21470/// child of the reparse root whose callee names the class can only be the
21471/// constructor: a member with no return type is nothing else.
21472fn cpp_reparsed_bare_constructor_call<'tree>(
21473    node: Node<'tree>,
21474    class_name: &str,
21475    source: &str,
21476) -> Option<Node<'tree>> {
21477    if node.kind() != "expression_statement" {
21478        return None;
21479    }
21480    let call = node
21481        .named_child(0)
21482        .filter(|child| child.kind() == "call_expression")?;
21483    let function = call.child_by_field_name("function")?;
21484    (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21485        .then_some(call)
21486}
21487
21488fn cpp_reparsed_defaulted_constructor_is_indexable(
21489    node: Node<'_>,
21490    members: &[Node<'_>],
21491    class_name: Option<&str>,
21492    source: &str,
21493) -> bool {
21494    let Some(class_name) = class_name else {
21495        return false;
21496    };
21497    if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21498        return false;
21499    }
21500    let Some(assignment) = node.named_child(0) else {
21501        return false;
21502    };
21503    if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21504        return false;
21505    }
21506    if assignment
21507        .child(1)
21508        .is_none_or(|operator| operator.kind() != "=")
21509    {
21510        return false;
21511    }
21512    let Some(call) = assignment.child_by_field_name("left") else {
21513        return false;
21514    };
21515    let Some(default_value) = assignment.child_by_field_name("right") else {
21516        return false;
21517    };
21518    let Some(function) = call.child_by_field_name("function") else {
21519        return false;
21520    };
21521    let Some(arguments) = call.child_by_field_name("arguments") else {
21522        return false;
21523    };
21524    if call.kind() != "call_expression"
21525        || function.kind() != "identifier"
21526        || arguments.kind() != "argument_list"
21527        || arguments.named_child_count() != 0
21528        || default_value.kind() != "identifier"
21529        || node_text(default_value, source) != "default"
21530        || node_text(function, source) != class_name
21531    {
21532        return false;
21533    }
21534    members.iter().any(|member| {
21535        if same_node(*member, node) {
21536            return false;
21537        }
21538        cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21539    })
21540}
21541
21542/// Return the constructor name from one direct class-member-shaped reparse
21543/// item. The function-declarator in an `ERROR` is admitted only through the
21544/// existing ERROR-only member proof; clean function definitions must carry no
21545/// return type and must retain either a body or ABI 15's `default_method_clause`.
21546fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21547    let function_declarator = match node.kind() {
21548        "function_definition"
21549            if !node.has_error() && node.child_by_field_name("type").is_none() =>
21550        {
21551            let has_body = node
21552                .child_by_field_name("body")
21553                .is_some_and(|body| body.kind() == "compound_statement");
21554            let has_default_method_clause = node
21555                .named_children(&mut node.walk())
21556                .any(|child| child.kind() == "default_method_clause");
21557            if !has_body && !has_default_method_clause {
21558                return None;
21559            }
21560            node.child_by_field_name("declarator")
21561                .and_then(extract_function_declarator)
21562        }
21563        "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21564            let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21565            let mut candidate = None;
21566            while let Some(current) = stack.pop() {
21567                if current.kind() == "function_declarator" {
21568                    if candidate.is_some() {
21569                        return None;
21570                    }
21571                    candidate = Some(current);
21572                    continue;
21573                }
21574                if current.kind() == "ERROR" {
21575                    let mut cursor = current.walk();
21576                    stack.extend(current.named_children(&mut cursor));
21577                }
21578            }
21579            candidate
21580        }
21581        _ => None,
21582    }?;
21583    cpp_function_declarator_name_node(function_declarator)
21584        .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21585}
21586
21587fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21588    cpp_reparsed_members_are_indexable_for_class(root, source, None)
21589}
21590
21591fn cpp_reparsed_members_are_indexable_for_class(
21592    root: Node<'_>,
21593    source: &str,
21594    class_name: Option<&str>,
21595) -> bool {
21596    let mut saw_member = false;
21597    let mut regions = Vec::new();
21598    let mut region_tree: Option<Tree> = None;
21599    loop {
21600        let root = region_tree.as_ref().map_or(root, Tree::root_node);
21601        let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
21602        let mut index = 0;
21603        while index < children.len() {
21604            let child = children[index];
21605            if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21606                saw_member = true;
21607                index += 3;
21608                continue;
21609            }
21610            if let Some(recovered) = fragmented_class_body(child, source) {
21611                let Some(tree) = cpp_reparse_fragmented_class_body(
21612                    source,
21613                    recovered.body.reparse_start,
21614                    recovered.body.reparse_end,
21615                ) else {
21616                    return false;
21617                };
21618                if !cpp_reparsed_members_are_indexable_for_class(
21619                    tree.root_node(),
21620                    source,
21621                    Some(recovered.name.as_str()),
21622                ) {
21623                    return false;
21624                }
21625                saw_member = true;
21626                index += 1;
21627                while index < children.len()
21628                    && children[index].end_byte() <= recovered.body.class_range.end_byte
21629                {
21630                    index += 1;
21631                }
21632                continue;
21633            }
21634            match child.kind() {
21635                "comment" => {}
21636                "labeled_statement" => {
21637                    let Some(label) = child.child_by_field_name("label") else {
21638                        return false;
21639                    };
21640                    if !matches!(node_text(label, source), "public" | "protected" | "private") {
21641                        return false;
21642                    }
21643                    let payload = child
21644                        .named_children(&mut child.walk())
21645                        .filter(|node| node.id() != label.id() && node.kind() != "comment")
21646                        .collect::<Vec<_>>();
21647                    if payload.is_empty() {
21648                        return false;
21649                    }
21650                    // The statement-context grammar splits these constructors
21651                    // across an ERROR and initializer-looking declarations.
21652                    if class_name.is_some_and(|name| {
21653                        cpp_reparsed_synthetic_initializer_constructor(
21654                            child,
21655                            name,
21656                            source,
21657                            child.end_byte(),
21658                        )
21659                        .is_some()
21660                            || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
21661                    }) {
21662                        saw_member = true;
21663                        index += 1;
21664                        continue;
21665                    }
21666                    // A destructor in statement context is a unary '~' call,
21667                    // with a possible stranded virtual keyword ahead of it.
21668                    let destructor = match payload.as_slice() {
21669                        [expression] => Some(*expression),
21670                        [specifier, expression]
21671                            if specifier.kind() == "ERROR"
21672                                && specifier.child_count() == 1
21673                                && specifier
21674                                    .child(0)
21675                                    .is_some_and(|keyword| keyword.kind() == "virtual") =>
21676                        {
21677                            Some(*expression)
21678                        }
21679                        _ => None,
21680                    };
21681                    if destructor.is_some_and(|expression| {
21682                        expression.kind() == "expression_statement"
21683                            && !expression.has_error()
21684                            && expression.named_child(0).is_some_and(|unary| {
21685                                unary.kind() == "unary_expression"
21686                                    && unary
21687                                        .child_by_field_name("operator")
21688                                        .is_some_and(|operator| operator.kind() == "~")
21689                                    && unary.child_by_field_name("argument").is_some_and(|call| {
21690                                        call.kind() == "call_expression"
21691                                            && call.child_by_field_name("function").is_some_and(
21692                                                |name| {
21693                                                    name.kind() == "identifier"
21694                                                        && class_name.is_none_or(|owner| {
21695                                                            node_text(name, source) == owner
21696                                                        })
21697                                                },
21698                                            )
21699                                            && call.child_by_field_name("arguments").is_some_and(
21700                                                |arguments| arguments.named_child_count() == 0,
21701                                            )
21702                                    })
21703                            })
21704                    }) {
21705                        saw_member = true;
21706                    } else {
21707                        // A label puts its payload in statement context, where
21708                        // static assertions look like calls and alias templates
21709                        // can become ERROR nodes. Reparse after the colon as
21710                        // declarations and apply this same gate to that region.
21711                        let colon = child
21712                            .children(&mut child.walk())
21713                            .find(|node| node.kind() == ":")
21714                            .expect("an access label has a colon");
21715                        regions.push(colon.end_byte()..child.end_byte());
21716                    }
21717                }
21718                "function_definition" => {
21719                    if child.has_error()
21720                        && !cpp_reparsed_member_function_is_indexable(child, source)
21721                        && cpp_sentinel_macro_region(child, source).is_none()
21722                    {
21723                        return false;
21724                    }
21725                    saw_member = true;
21726                }
21727                // The attribute a string-argument macro leaves as a call statement
21728                // of its own. It declares nothing; the member it decorated is the
21729                // sibling after it, checked on its own turn.
21730                "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
21731                "expression_statement"
21732                    if cpp_reparsed_defaulted_constructor_is_indexable(
21733                        child, &children, class_name, source,
21734                    ) =>
21735                {
21736                    saw_member = true;
21737                }
21738                "expression_statement"
21739                    if class_name.is_some_and(|class_name| {
21740                        cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21741                    }) =>
21742                {
21743                    saw_member = true;
21744                }
21745                "ERROR"
21746                    if (cpp_reparsed_member_error_is_indexable(child)
21747                        || cpp_reparsed_adjacent_copy_control_error(child, source)
21748                        || cpp_reparsed_stranded_member_error(child, source))
21749                        && (child
21750                        .next_named_sibling()
21751                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21752                        || cpp_reparsed_member_error_with_preprocessed_body(child)
21753                        // The macro-declaration grammar folds the member after
21754                        // the copy-control pair into the same ERROR and leaves
21755                        // its body as the following sibling.
21756                        || (cpp_reparsed_adjacent_copy_control_error(child, source)
21757                            && child
21758                                .next_named_sibling()
21759                                .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21760                {
21761                    saw_member = true;
21762                }
21763                "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21764                    saw_member = true;
21765                }
21766                "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21767                    saw_member = true;
21768                }
21769                "expression_statement"
21770                    if cpp_is_stray_semicolon(child, source)
21771                        && child.prev_named_sibling().is_some_and(|error| {
21772                            cpp_reparsed_member_error_is_indexable(error)
21773                                || cpp_reparsed_adjacent_copy_control_error(error, source)
21774                                || cpp_reparsed_stranded_member_error(error, source)
21775                        }) =>
21776                {
21777                    saw_member = true;
21778                }
21779                "compound_statement"
21780                    if cpp_reparsed_constructor_body_is_indexable(child, source)
21781                        || cpp_reparsed_attribute_requires_body(child, source)
21782                        || child.prev_named_sibling().is_some_and(|error| {
21783                            cpp_reparsed_adjacent_copy_control_error(error, source)
21784                        }) =>
21785                {
21786                    saw_member = true;
21787                }
21788                // A copy-control declaration the macro-declaration grammar reads
21789                // as a function-like macro wrapping a type: `XMLElement( const
21790                // XMLElement& )` in a statement-context reparse is a
21791                // `macro_type_specifier`, admitted only where the parse would
21792                // otherwise contain an error. The live `;` beside it is the
21793                // declaration's own terminator, the same proof the ERROR
21794                // envelope's stray semicolon carried.
21795                "macro_type_specifier"
21796                    if child
21797                        .child_by_field_name("name")
21798                        .is_some_and(|name| name.kind() == "identifier")
21799                        && child
21800                            .child_by_field_name("type")
21801                            .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21802                        && child.next_sibling().is_some_and(|sibling| {
21803                            sibling.kind() == ";" && !sibling.is_missing()
21804                        }) =>
21805                {
21806                    saw_member = true;
21807                }
21808                kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21809                _ => return false,
21810            }
21811            index += 1;
21812        }
21813        let Some(region) = regions.pop() else {
21814            return saw_member;
21815        };
21816        let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
21817            return false;
21818        };
21819        region_tree = Some(tree);
21820    }
21821}
21822
21823/// Detect the malformed constructor shape that tree-sitter exposes as an
21824/// access-label statement followed by initializer-looking declarations. The
21825/// declarations are not class members: visiting their `location(loc)` and
21826/// `string(s)` function declarators would publish synthetic functions. The
21827/// export-class fallback keeps the original sibling nodes and therefore avoids
21828/// this parser artifact. The returned range identifies the real constructor
21829/// header, which can be reparsed independently as a structured declarator.
21830fn cpp_reparsed_synthetic_initializer_constructor_range(
21831    root: Node<'_>,
21832    class_name: &str,
21833    source: &str,
21834    constructor_end: usize,
21835) -> Option<std::ops::Range<usize>> {
21836    let mut stack = {
21837        let mut cursor = root.walk();
21838        root.named_children(&mut cursor).collect::<Vec<_>>()
21839    };
21840    while let Some(current) = stack.pop() {
21841        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21842            current,
21843            class_name,
21844            source,
21845            constructor_end,
21846        ) {
21847            return Some(range);
21848        }
21849        if current.kind() == "ERROR" {
21850            let mut cursor = current.walk();
21851            stack.extend(current.named_children(&mut cursor));
21852        }
21853    }
21854    None
21855}
21856
21857/// Recover an inline constructor that a function-like export macro makes
21858/// tree-sitter merge with the following overload. In the reparsed class-body
21859/// region, the access label wraps one declaration whose ERROR contains the
21860/// constructor declarator and its base-initializer/body, while the declaration's
21861/// ordinary declarator is the following overload. Every boundary below comes
21862/// from that CST; no source syntax is reparsed by hand.
21863fn cpp_reparsed_merged_inline_constructor<'tree>(
21864    root: Node<'tree>,
21865    class_name: &str,
21866    source: &str,
21867) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21868    let mut stack = vec![root];
21869    while let Some(current) = stack.pop() {
21870        if current.kind() != "labeled_statement" {
21871            let mut cursor = current.walk();
21872            stack.extend(current.named_children(&mut cursor));
21873            continue;
21874        }
21875        let Some(declaration) = current
21876            .named_children(&mut current.walk())
21877            .find(|child| child.kind() == "declaration")
21878        else {
21879            continue;
21880        };
21881        if declaration
21882            .child_by_field_name("type")
21883            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21884        {
21885            continue;
21886        }
21887        let following = declaration
21888            .child_by_field_name("declarator")
21889            .and_then(extract_function_declarator)
21890            .and_then(cpp_function_declarator_name_node);
21891        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21892            continue;
21893        }
21894        let mut declaration_cursor = declaration.walk();
21895        let Some(error) = declaration
21896            .named_children(&mut declaration_cursor)
21897            .find(|child| child.kind() == "ERROR")
21898        else {
21899            continue;
21900        };
21901        let mut error_cursor = error.walk();
21902        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21903        let Some(constructor) = error_children.iter().copied().find(|child| {
21904            child.kind() == "function_declarator"
21905                && cpp_function_declarator_name_node(*child)
21906                    .is_some_and(|name| node_text(name, source).trim() == class_name)
21907        }) else {
21908            continue;
21909        };
21910        let Some(body) = error_children.iter().copied().find_map(|child| {
21911            (child.kind() == "init_declarator")
21912                .then(|| child.child_by_field_name("value"))
21913                .flatten()
21914                .filter(|value| value.kind() == "initializer_list")
21915        }) else {
21916            continue;
21917        };
21918        if constructor.end_byte() > body.start_byte() {
21919            continue;
21920        }
21921        return Some((constructor.start_byte()..body.end_byte(), body));
21922    }
21923    None
21924}
21925
21926fn cpp_reparsed_synthetic_initializer_constructor(
21927    node: Node<'_>,
21928    class_name: &str,
21929    source: &str,
21930    constructor_end: usize,
21931) -> Option<std::ops::Range<usize>> {
21932    if node.kind() != "labeled_statement" {
21933        return None;
21934    }
21935    let mut cursor = node.walk();
21936    let named = node
21937        .named_children(&mut cursor)
21938        .filter(|child| child.kind() != "comment")
21939        .collect::<Vec<_>>();
21940    let label = named.first()?;
21941    if label.kind() != "statement_identifier"
21942        || !matches!(
21943            node_text(*label, source).trim(),
21944            "public" | "private" | "protected"
21945        )
21946    {
21947        return None;
21948    }
21949    let call_error_index = named.iter().position(|child| {
21950        if child.kind() != "ERROR" {
21951            return false;
21952        }
21953        // The macro-declaration grammar keeps the constructor head's type as
21954        // the ERROR's own type field; the older grammar flattened the head
21955        // into a call expression inside it. ERROR nodes retain field labels
21956        // only by child index in runtime 0.25.
21957        let type_child = (0..child.child_count()).find_map(|index| {
21958            let candidate = child.child(index)?;
21959            (child.field_name_for_child(index as u32) == Some("type")
21960                && candidate.kind() == "type_identifier"
21961                && node_text(candidate, source).trim() == class_name)
21962                .then_some(candidate)
21963        });
21964        if type_child.is_some() {
21965            return true;
21966        }
21967        let mut stack = vec![*child];
21968        while let Some(current) = stack.pop() {
21969            if current.kind() == "call_expression"
21970                && current
21971                    .child_by_field_name("function")
21972                    .is_some_and(|function| {
21973                        function.kind() == "identifier"
21974                            && node_text(function, source).trim() == class_name
21975                    })
21976            {
21977                return true;
21978            }
21979            let mut cursor = current.walk();
21980            stack.extend(current.named_children(&mut cursor));
21981        }
21982        false
21983    })?;
21984    let constructor_start = {
21985        let head = named[call_error_index];
21986        let type_child = (0..head.child_count()).find_map(|index| {
21987            let candidate = head.child(index)?;
21988            (head.field_name_for_child(index as u32) == Some("type")
21989                && candidate.kind() == "type_identifier"
21990                && node_text(candidate, source).trim() == class_name)
21991                .then_some(candidate)
21992        });
21993        if let Some(type_node) = type_child {
21994            type_node.start_byte()
21995        } else {
21996            let mut stack = vec![head];
21997            let mut found = None;
21998            while let Some(current) = stack.pop() {
21999                if current.kind() == "call_expression"
22000                    && current
22001                        .child_by_field_name("function")
22002                        .is_some_and(|function| {
22003                            function.kind() == "identifier"
22004                                && node_text(function, source).trim() == class_name
22005                        })
22006                {
22007                    found = Some(current);
22008                    break;
22009                }
22010                let mut cursor = current.walk();
22011                stack.extend(current.named_children(&mut cursor));
22012            }
22013            found?.start_byte()
22014        }
22015    };
22016    named.iter().skip(call_error_index + 1).find(|child| {
22017        child.kind() == "declaration" && child.has_error() && {
22018            let mut cursor = child.walk();
22019            child.named_children(&mut cursor).any(|declarator| {
22020                declarator.kind() == "init_declarator"
22021                    && declarator
22022                        .child_by_field_name("declarator")
22023                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
22024                    && declarator
22025                        .child_by_field_name("value")
22026                        .is_some_and(|value| value.kind() == "initializer_list")
22027            })
22028        }
22029    })?;
22030    Some(constructor_start..constructor_end)
22031}
22032
22033fn cpp_reparsed_exact_constructor_declarator<'tree>(
22034    root: Node<'tree>,
22035    start: usize,
22036    class_name: &str,
22037    source: &str,
22038) -> Option<Node<'tree>> {
22039    let mut candidate = None;
22040    let mut stack = vec![root];
22041    while let Some(current) = stack.pop() {
22042        if current.kind() == "function_declarator"
22043            && current.start_byte() == start
22044            && cpp_function_declarator_name_node(current)
22045                .is_some_and(|name| node_text(name, source).trim() == class_name)
22046        {
22047            if candidate.is_some() {
22048                return None;
22049            }
22050            candidate = Some(current);
22051            continue;
22052        }
22053        let mut cursor = current.walk();
22054        stack.extend(current.named_children(&mut cursor));
22055    }
22056    candidate
22057}
22058
22059fn cpp_is_indexable_item_kind(kind: &str) -> bool {
22060    matches!(
22061        kind,
22062        "namespace_definition"
22063            | "class_specifier"
22064            | "struct_specifier"
22065            | "union_specifier"
22066            | "enum_specifier"
22067            | "function_definition"
22068            | "template_declaration"
22069            | "declaration"
22070            | "field_declaration"
22071            | "alias_declaration"
22072            | "static_assert_declaration"
22073            | "type_definition"
22074            | "using_declaration"
22075            | "linkage_specification"
22076            | "preproc_def"
22077            | "preproc_function_def"
22078            | "preproc_include"
22079            | "preproc_if"
22080            | "preproc_ifdef"
22081            | "preproc_call"
22082    )
22083}
22084
22085fn cpp_overload_discriminator(declarator: Node<'_>, source: &str) -> [u8; 32] {
22086    use brokk_bifrost_core::analyzer::tree_walk::{
22087        canonical_type_tokens, overload_discriminator, overload_parameter_tokens,
22088    };
22089    let mut tokens = Vec::new();
22090    if let Some(parameters) = declarator.child_by_field_name("parameters") {
22091        for slot in cpp_callable_parameter_slots(parameters, source) {
22092            match slot {
22093                CppParameterSlot::Declared { parameter, .. } => {
22094                    tokens.extend(overload_parameter_tokens(
22095                        parameter.child_by_field_name("type"),
22096                        source,
22097                    ));
22098                    let mut cursor = parameter.walk();
22099                    for qualifier in parameter.named_children(&mut cursor) {
22100                        if qualifier.kind() == "type_qualifier" {
22101                            tokens.extend(canonical_type_tokens(qualifier, source));
22102                        }
22103                    }
22104                    // Pointer/reference/array/function declarators carry type
22105                    // structure outside the base type. Exclude only the name.
22106                    if let Some(declarator) = parameter.child_by_field_name("declarator") {
22107                        let name = crate::structural::declarator_name_node(declarator);
22108                        let mut pending = vec![(declarator, name)];
22109                        while let Some((node, excluded_name)) = pending.pop() {
22110                            if Some(node) == excluded_name {
22111                                continue;
22112                            }
22113                            if node.child_count() == 0 {
22114                                tokens.extend(canonical_type_tokens(node, source));
22115                            } else {
22116                                let parameter = matches!(
22117                                    node.kind(),
22118                                    "parameter_declaration" | "optional_parameter_declaration"
22119                                );
22120                                let excluded_name = if parameter {
22121                                    node.child_by_field_name("declarator")
22122                                        .and_then(crate::structural::declarator_name_node)
22123                                } else {
22124                                    excluded_name
22125                                };
22126                                let default = parameter
22127                                    .then(|| node.child_by_field_name("default_value"))
22128                                    .flatten();
22129                                for index in (0..node.child_count()).rev() {
22130                                    let child = node.child(index).expect("child");
22131                                    if Some(child) != default {
22132                                        pending.push((child, excluded_name));
22133                                    }
22134                                }
22135                            }
22136                        }
22137                    }
22138                }
22139                CppParameterSlot::Ellipsis => tokens.push("<ellipsis>".to_owned()),
22140                CppParameterSlot::OptionalVoid => {}
22141            }
22142        }
22143    }
22144    let mut cursor = declarator.walk();
22145    let mut qualifiers = declarator
22146        .named_children(&mut cursor)
22147        .filter(|node| node.kind() == "type_qualifier")
22148        .map(|node| canonical_type_tokens(node, source))
22149        .collect::<Vec<_>>();
22150    qualifiers.sort();
22151    for qualifier in qualifiers {
22152        tokens.push("<member-qualifier>".to_owned());
22153        tokens.extend(qualifier);
22154    }
22155    for qualifier in declarator.named_children(&mut cursor) {
22156        if qualifier.kind() == "ref_qualifier" {
22157            tokens.push("<member-qualifier>".to_owned());
22158            tokens.extend(canonical_type_tokens(qualifier, source));
22159        }
22160    }
22161    overload_discriminator(tokens)
22162}
22163
22164fn cpp_synthetic_name(node: Node<'_>) -> brokk_bifrost_core::analyzer::tree_walk::SyntheticName {
22165    brokk_bifrost_core::analyzer::tree_walk::synthetic_name(
22166        node,
22167        |candidate| match candidate.kind() {
22168            "compound_statement" => Some("block"),
22169            "struct_specifier" | "union_specifier" | "class_specifier"
22170                if candidate.child_by_field_name("name").is_none() =>
22171            {
22172                Some("anonymous")
22173            }
22174            _ => None,
22175        },
22176        |candidate| {
22177            candidate.kind() == "function_definition"
22178                || matches!(
22179                    candidate.kind(),
22180                    "class_specifier"
22181                        | "struct_specifier"
22182                        | "union_specifier"
22183                        | "namespace_definition"
22184                ) && candidate.child_by_field_name("name").is_some()
22185        },
22186    )
22187}
22188
22189#[cfg(test)]
22190mod tests {
22191
22192    #[test]
22193    fn recovered_callable_grammar_and_original_bytes() {
22194        use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
22195
22196        let parsed = tree(SOURCE);
22197        // These are the current grammar's contracts, including the macro_modifier
22198        // support that has replaced some older ERROR/argument_list recoveries.
22199        // Each row selects a real source region and independently pins its fields
22200        // and the tokens that must survive a synthetic class-context reparse.
22201        let cases = [
22202            (
22203                "expression_statement",
22204                58..72,
22205                &[][..],
22206                &[(58..64, "Widget"), (65..70, "value")][..],
22207            ),
22208            (
22209                "field_declaration",
22210                92..145,
22211                &[
22212                    ("type", "qualified_identifier", 111..122),
22213                    ("declarator", "function_declarator", 123..144),
22214                ][..],
22215                &[
22216                    (123..131, "toString"),
22217                    (132..137, "Yield"),
22218                    (138..143, "yield"),
22219                ][..],
22220            ),
22221            (
22222                "function_definition",
22223                147..191,
22224                &[
22225                    ("type", "qualified_identifier", 151..161),
22226                    ("declarator", "parenthesized_declarator", 161..174),
22227                    ("body", "compound_statement", 189..191),
22228                ][..],
22229                &[
22230                    (155..161, "Widget"),
22231                    (162..167, "Value"),
22232                    (168..173, "value"),
22233                ][..],
22234            ),
22235            (
22236                "field_declaration",
22237                193..296,
22238                &[
22239                    ("type", "macro_type_specifier", 193..222),
22240                    ("declarator", "macro_modifier_declarator", 223..263),
22241                    ("default_value", "initializer_list", 291..293),
22242                ][..],
22243                &[
22244                    (233..239, "Widget"),
22245                    (240..248, "grow_fun"),
22246                    (249..253, "grow"),
22247                ][..],
22248            ),
22249            (
22250                "call_expression",
22251                58..71,
22252                &[
22253                    ("function", "identifier", 58..64),
22254                    ("arguments", "argument_list", 64..71),
22255                ][..],
22256                &[][..],
22257            ),
22258            (
22259                "function_declarator",
22260                123..144,
22261                &[
22262                    ("declarator", "field_identifier", 123..131),
22263                    ("parameters", "parameter_list", 131..144),
22264                ][..],
22265                &[][..],
22266            ),
22267            (
22268                "function_declarator",
22269                233..263,
22270                &[
22271                    ("declarator", "field_identifier", 233..239),
22272                    ("parameters", "parameter_list", 239..254),
22273                ][..],
22274                &[][..],
22275            ),
22276        ];
22277        for (kind, span, fields, tokens) in cases {
22278            let node = at(parsed.root_node(), kind, span.clone());
22279            for (name, child_kind, child_span) in fields {
22280                field(node, name, child_kind, child_span.clone());
22281            }
22282            if tokens.is_empty() {
22283                continue;
22284            }
22285            // Match visit_bare_constructor_expression's documented class context.
22286            // The initializer-list body bounds the initialized-constructor region;
22287            // its recovery field also swallowed the original class close.
22288            let end = node
22289                .child_by_field_name("default_value")
22290                .map_or(span.end, |body| body.end_byte());
22291            const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22292            let parsed = crate::graph::syntax::parse_sentinel(
22293                PREFIX,
22294                crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22295                " };",
22296            );
22297            let (synthetic, reparsed) = (parsed.source, parsed.tree);
22298            for (original_span, text) in tokens {
22299                assert_eq!(&SOURCE[original_span.clone()], *text);
22300                let translated = PREFIX.len() + original_span.start - span.start
22301                    ..PREFIX.len() + original_span.end - span.start;
22302                let leaves: Vec<_> = nodes(reparsed.root_node())
22303                    .into_iter()
22304                    .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22305                    .collect();
22306                assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22307                let token = leaves[0];
22308                assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22309                assert_eq!(
22310                    token.start_byte() - PREFIX.len() + span.start
22311                        ..token.end_byte() - PREFIX.len() + span.start,
22312                    original_span.clone()
22313                );
22314                assert!(SOURCE.is_char_boundary(original_span.start));
22315                assert!(SOURCE.is_char_boundary(original_span.end));
22316            }
22317        }
22318    }
22319
22320    use super::*;
22321    use crate::adapter::parse_cpp_file;
22322    use brokk_bifrost_core::analyzer::parsed_file::{
22323        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22324    };
22325    use brokk_bifrost_test_macros::hardwon;
22326    use std::fmt::Write;
22327
22328    #[test]
22329    fn synthetic_declaration_ids_follow_rank_across_edits() {
22330        fn ids(source: &str) -> std::collections::BTreeSet<String> {
22331            let parsed = parse_cpp_declarations(source, "rank.cpp");
22332            parsed
22333                .declarations()
22334                .iter()
22335                .filter(|unit| unit.identifier() == "Target")
22336                .map(|unit| unit.declaration_id().to_string())
22337                .collect()
22338        }
22339        let source = "void f(){ { class Target {}; } }";
22340        let original = ids(source);
22341        assert!(!original.is_empty(), "target declarations were produced");
22342        assert_eq!(original, ids(&format!("// byte shift\n{source}")));
22343        let inserted = ids("void f(){ {} { class Target {}; } }");
22344        assert_eq!(original.len(), inserted.len());
22345        assert!(
22346            original.is_disjoint(&inserted),
22347            "earlier siblings change ranks"
22348        );
22349    }
22350
22351    #[test]
22352    fn overload_synthetic_declarations_keep_distinct_ids() {
22353        let parsed = parse_cpp_declarations(
22354            "struct Owner { void f(int) { { class Marker {}; } } void f(double) { { class Marker {}; } } };",
22355            "overloads.cpp",
22356        );
22357        let ids: Vec<_> = parsed
22358            .declarations()
22359            .iter()
22360            .filter(|unit| unit.identifier() == "Marker")
22361            .map(|unit| unit.declaration_id())
22362            .collect();
22363        assert_eq!(ids.len(), 2, "both overloads keep their local declaration");
22364        assert_ne!(
22365            ids[0], ids[1],
22366            "local declarations have distinct identities"
22367        );
22368    }
22369
22370    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22371        let tree = crate::diagnostics::parse_cpp_tree(source);
22372        let file = ProjectFile::new(std::env::temp_dir(), name);
22373        parse_cpp_file(&file, source, &tree)
22374    }
22375
22376    #[test]
22377    #[hardwon(
22378        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22379    )]
22380    fn comment_only_enums_have_no_enumerators() {
22381        let parsed =
22382            parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22383        let names: Vec<_> = parsed
22384            .declarations()
22385            .iter()
22386            .map(|unit| unit.fq_name())
22387            .collect();
22388        assert_eq!(names, ["Empty"]);
22389    }
22390
22391    #[test]
22392    #[hardwon(
22393        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22394    )]
22395    fn quicksilver_gnu_attribute_orders() {
22396        for attributes in [
22397            "deprecated, warn_unused_result",
22398            "warn_unused_result, deprecated",
22399        ] {
22400            let source = format!("int value() __attribute__(({attributes}));");
22401            let tree = crate::diagnostics::parse_cpp_tree(&source);
22402            assert!(
22403                !tree.root_node().has_error(),
22404                "{}",
22405                tree.root_node().to_sexp()
22406            );
22407            let declaration = tree.root_node().named_child(0).unwrap();
22408            assert!(
22409                cpp_declaration_has_native_must_use_attribute(declaration, &source),
22410                "{source}: {}",
22411                declaration.to_sexp()
22412            );
22413        }
22414    }
22415
22416    #[test]
22417    #[hardwon(
22418        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22419    )]
22420    fn fragmented_brace_matching_ignores_literal_tokens() {
22421        // Isolate the brace proof from export-head recognition: the damaged
22422        // field list supplies the same container boundary used by recovery.
22423        let source =
22424            r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22425        let tree = crate::diagnostics::parse_cpp_tree(source);
22426        let tokens = crate::grammar_test_support::nodes(tree.root_node());
22427        assert!(
22428            tokens
22429                .iter()
22430                .any(|node| node.kind() == "raw_string_literal")
22431        );
22432        let body = tokens
22433            .iter()
22434            .find(|node| node.kind() == "field_declaration_list")
22435            .unwrap();
22436        let opening = body.child(0).unwrap();
22437        let head = body.named_child(0).unwrap();
22438        assert_eq!(opening.kind(), "{");
22439        assert!(body.has_error());
22440        assert_eq!(
22441            fragmented_container_close_class_body(head, opening, source)
22442                .map(|body| body.reparse_end),
22443            source.rfind('}')
22444        );
22445    }
22446
22447    #[test]
22448    #[hardwon(
22449        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22450    )]
22451    fn quicksilver_uppercase_attributed_classes() {
22452        for (name, body) in [
22453            ("Box", "{ int value; }"),
22454            ("BOX", "{ int value; }"),
22455            ("API", ""),
22456        ] {
22457            let source = format!("class [[deprecated]] {name} {body};");
22458            let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22459            let names = parsed
22460                .declarations()
22461                .iter()
22462                .filter(|unit| unit.is_class())
22463                .map(|unit| unit.identifier().to_owned())
22464                .collect::<Vec<_>>();
22465            assert_eq!(names, [name], "{source}");
22466        }
22467    }
22468
22469    #[test]
22470    #[hardwon(
22471        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22472    )]
22473    fn quicksilver_fragmented_export_uppercase_classes() {
22474        for name in ["Box", "BOX"] {
22475            let source = r#"
22476namespace api {
22477
22478/**
22479* Doc comment
22480*/
22481class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22482   public:
22483      /**
22484      * Construct from a point.
22485      */
22486      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22487
22488#if defined(PROJECT_HAS_LEGACY_POINT)
22489      /**
22490      * Construct from a legacy point.
22491      */
22492      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22493#endif
22494
22495      std::string algo_name() const override;
22496
22497      AlgorithmIdentifier algorithm_identifier() const override;
22498};
22499
22500}
22501"#
22502            .replace("VirtualBased", name);
22503            // Positional recovery once named this class. Since
22504            // brokk-tree-sitter-cpp 0.24.7 the grammar reads the head, and an
22505            // uppercase name must survive that reading too.
22506            let tree = crate::grammar_test_support::tree(&source);
22507            crate::grammar_test_support::assert_function_like_export_class_head(
22508                &tree, &source, name,
22509            );
22510            let parsed = parse_cpp_declarations(&source, "virtual-base.hpp");
22511            let explicit = parse_cpp_declarations(
22512                &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
22513                "virtual-base.hpp",
22514            );
22515            assert_eq!(
22516                declaration_expansion_inventory(&parsed),
22517                declaration_expansion_inventory(&explicit),
22518                "{name}"
22519            );
22520            let declarations = parsed.declarations();
22521            let class = declarations
22522                .iter()
22523                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22524                .unwrap_or_else(|| panic!("{name}: missing class: {declarations:#?}"));
22525            assert_eq!(
22526                parsed.raw_supertypes.get(class),
22527                Some(&vec!["BaseKey".to_string()]),
22528                "{name}"
22529            );
22530            assert!(
22531                declarations.iter().any(|unit| {
22532                    unit.is_function() && unit.fq_name() == format!("api.{name}.algo_name")
22533                }),
22534                "{name}: the class must own its members: {declarations:#?}"
22535            );
22536        }
22537    }
22538
22539    #[test]
22540    #[hardwon(
22541        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22542    )]
22543    fn quicksilver_constructor_search_skips_other_labels() {
22544        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";
22545        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22546        let (range, body) =
22547            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22548                .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22549        assert_eq!(
22550            &source[range],
22551            "Lookup_Error(std::string_view err) : Exception(err) {}"
22552        );
22553        assert_eq!(node_text(body, source), "{}");
22554    }
22555
22556    #[test]
22557    #[hardwon(
22558        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
22559    )]
22560    fn quicksilver_member_gate_checks_label_payload() {
22561        for (source, expected) in [
22562            ("public: int value;", true),
22563            (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
22564            (
22565                "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
22566                true,
22567            ),
22568            ("public: do_work();", false),
22569            ("ordinary: int value;", false),
22570        ] {
22571            let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22572            assert_eq!(
22573                cpp_reparsed_members_are_indexable(tree.root_node(), source),
22574                expected,
22575                "{source}: {}",
22576                tree.root_node().to_sexp()
22577            );
22578        }
22579    }
22580
22581    #[test]
22582    #[hardwon(
22583        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
22584    )]
22585    fn quicksilver_sentinel_uppercase_classes() {
22586        for name in ["Box", "BOX"] {
22587            let source = format!(
22588                "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"
22589            );
22590            let tree = crate::diagnostics::parse_cpp_tree(&source);
22591            let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
22592            assert!(
22593                recovered
22594                    .iter()
22595                    .any(|class| class.scope_components == ["absl", "container_internal", name]),
22596                "{source}: {recovered:#?}"
22597            );
22598        }
22599    }
22600
22601    #[test]
22602    fn native_must_use_attribute_nodes_are_structural() {
22603        let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
22604        let tree = crate::diagnostics::parse_cpp_tree(source);
22605        let root = tree.root_node();
22606        let mut declarations = Vec::new();
22607        walk_named_tree_preorder(root, true, |node| {
22608            if matches!(
22609                node.kind(),
22610                "declaration" | "function_definition" | "field_declaration"
22611            ) {
22612                declarations.push(node);
22613            }
22614            WalkControl::Continue
22615        });
22616        assert_eq!(
22617            declarations
22618                .into_iter()
22619                .map(|declaration| {
22620                    cpp_declaration_has_native_must_use_attribute(declaration, source)
22621                })
22622                .collect::<Vec<_>>(),
22623            vec![true, false, true]
22624        );
22625        let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
22626        let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
22627        let mut enum_specifier = None;
22628        walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
22629            if node.kind() == "enum_specifier" {
22630                enum_specifier = Some(node);
22631                WalkControl::Break
22632            } else {
22633                WalkControl::Continue
22634            }
22635        });
22636        assert!(cpp_declaration_has_native_must_use_attribute(
22637            enum_specifier.expect("enum declaration"),
22638            enum_source
22639        ));
22640    }
22641
22642    fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
22643        let mut rows = parsed
22644            .signature_metadata
22645            .iter()
22646            .flat_map(|(unit, entries)| {
22647                entries.iter().map(move |metadata| {
22648                    assert!(
22649                        metadata.callable_modifiers_recorded(),
22650                        "{} must record that the walk read its declaration shape",
22651                        unit.fq_name()
22652                    );
22653                    format!(
22654                        "{} decl_only={} static={} constructor={} visibility={:?}",
22655                        unit.fq_name(),
22656                        metadata.is_declaration_only(),
22657                        metadata.callable_is_static(),
22658                        metadata.callable_is_constructor(),
22659                        metadata
22660                            .callable_declared_visibility()
22661                            .expect("a recorded modifier set states a visibility"),
22662                    )
22663                })
22664            })
22665            .collect::<Vec<_>>();
22666        rows.sort();
22667        rows
22668    }
22669
22670    /// #3520: the input-edge rebuild of a rendered C++ name must render back to
22671    /// exactly the spelling it was given, with the nested-class `$` recorded as
22672    /// a [`SegmentKind::Nested`] join and a member as its own tail segment.
22673    #[test]
22674    #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
22675    fn rendered_name_fq_round_trips_nested_class_spellings() {
22676        assert!(cpp_rendered_name_fq("").is_empty());
22677        for rendered in [
22678            "Only",
22679            "Top$Only",
22680            "ns.Outer$Inner",
22681            "Top$Only.b",
22682            "ns.Outer$Inner.a",
22683            "Buck$Stop",
22684        ] {
22685            let fq = cpp_rendered_name_fq(rendered);
22686            assert_eq!(
22687                fq.display_native(Language::Cpp, segment_interner()),
22688                rendered,
22689                "native round trip for {rendered}"
22690            );
22691        }
22692        let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
22693            .segments()
22694            .iter()
22695            .map(|segment| segment_interner().resolve(*segment).1)
22696            .collect();
22697        assert_eq!(
22698            kinds,
22699            [
22700                SegmentKind::Unknown,
22701                SegmentKind::Unknown,
22702                SegmentKind::Nested
22703            ]
22704        );
22705    }
22706
22707    /// #3631: a `::`-only source spelling must name the nested `$` identity
22708    /// and the namespace `.` identity. Canonical `.` / `$` spellings stay on
22709    /// the single rendered-name parse.
22710    #[test]
22711    #[hardwon(
22712        "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
22713    )]
22714    fn colon_source_aliases_name_nested_and_namespace_identities() {
22715        let native = |rendered: &str| {
22716            cpp_colon_source_aliases(rendered)
22717                .into_iter()
22718                .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
22719                .collect::<Vec<_>>()
22720        };
22721        let top = native("Top::Only");
22722        assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
22723        assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
22724        assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
22725        let nested = native("ns::Outer::Inner");
22726        assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
22727        assert!(
22728            nested.contains(&"ns::Outer.Inner".to_string()),
22729            "{nested:?}"
22730        );
22731        let member = native("Top::Only::b");
22732        assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
22733        assert!(cpp_colon_source_aliases("Top$Only").is_empty());
22734        assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
22735        assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
22736        assert!(cpp_colon_source_aliases("Only").is_empty());
22737        assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
22738            fq.segments()
22739                .iter()
22740                .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
22741        }));
22742    }
22743
22744    #[test]
22745    #[hardwon(
22746        "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
22747    )]
22748    fn displaced_terminator_ignores_nested_initializer_endif() {
22749        let source = "#ifdef ENABLE_ITEMS\n\
22750struct Item { int reg; };\n\
22751static const struct Item items[] = {{1}};\n\
22752static const struct Item extras[] = {\n\
22753  {0},\n\
22754#ifndef REDUCED\n\
22755  {1},\n\
22756#endif\n\
22757};\n\
22758int read_item(int i) { return items[i].reg; }\n\
22759#endif\n";
22760        let mut parser = tree_sitter::Parser::new();
22761        parser
22762            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22763            .expect("C++ grammar");
22764        let tree = parser.parse(source, None).expect("fixture tree");
22765        let conditional = tree
22766            .root_node()
22767            .named_child(0)
22768            .filter(|node| node.kind() == "preproc_ifdef")
22769            .expect("outer conditional");
22770
22771        assert!(conditional.has_error());
22772        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
22773        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
22774    }
22775
22776    #[test]
22777    #[hardwon(
22778        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22779    )]
22780    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
22781        let source = "struct Image { PyObject_HEAD Imaging image; };";
22782        let parsed = parse_cpp_declarations(source, "image.h");
22783        let names = parsed
22784            .declarations()
22785            .iter()
22786            .map(|unit| unit.fq_name())
22787            .collect::<Vec<_>>();
22788
22789        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
22790        assert!(
22791            names.iter().all(|name| name != "Image.Imaging"),
22792            "the pseudo-declarator must not become a field: {names:#?}"
22793        );
22794
22795        let pointer = parse_cpp_declarations(
22796            "struct Image { PyObject_HEAD Imaging *image; };",
22797            "image-pointer.h",
22798        );
22799        let pointer_names = pointer
22800            .declarations()
22801            .iter()
22802            .map(|unit| unit.fq_name())
22803            .collect::<Vec<_>>();
22804        assert!(
22805            pointer_names.iter().any(|name| name == "Image.image"),
22806            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
22807        );
22808        assert!(
22809            pointer_names.iter().all(|name| name != "Image.Imaging"),
22810            "the pointer recovery error must not become a field: {pointer_names:#?}"
22811        );
22812
22813        let unrelated_macro = parse_cpp_declarations(
22814            "struct Image { OTHER_HEAD Imaging other; };",
22815            "image-near-miss.h",
22816        );
22817        let unrelated_names = unrelated_macro
22818            .declarations()
22819            .iter()
22820            .map(|unit| unit.fq_name())
22821            .collect::<Vec<_>>();
22822        assert!(
22823            unrelated_names.iter().any(|name| name == "Image.other"),
22824            "an object-like macro in specifier position is a `macro_modifier`, so the \
22825             member is the structured best-effort reading, the same one the \
22826             macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
22827        );
22828        assert!(
22829            unrelated_names
22830                .iter()
22831                .all(|name| name != "Image.OTHER_HEAD"),
22832            "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
22833        );
22834    }
22835
22836    #[test]
22837    #[hardwon(
22838        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22839    )]
22840    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
22841        let source = r#"namespace testing {
22842namespace internal {
22843    namespace detail {
22844        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
22845        public:
22846            int value() const { return count_ + 1; }
22847        private:
22848            int count_;
22849        };
22850        class GTEST_API_ [[nodiscard]] OtherReporter {
22851        public:
22852            int value() const { return count_ + 2; }
22853        private:
22854            int count_;
22855        };
22856    }
22857
22858    template <typename T>
22859    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
22860
22861    class TailReporter {};
22862}
22863}
22864"#;
22865        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
22866        let declarations = parsed.declarations();
22867        let mut parser = tree_sitter::Parser::new();
22868        parser
22869            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22870            .unwrap();
22871        let tree = parser.parse(source, None).unwrap();
22872        let tail_start = source.find("TailReporter").expect("tail class");
22873        let tail_node = tree
22874            .root_node()
22875            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
22876            .expect("tail class AST node");
22877        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22878        assert!(
22879            tree.root_node().has_error(),
22880            "the malformed class must exercise recovery"
22881        );
22882        assert!(index.region_at(tail_start).is_some());
22883        assert_eq!(
22884            index.enclosing_namespace_components(tail_node, source),
22885            ["testing", "internal"]
22886        );
22887        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
22888        let mut recovered_parsed = ParsedFile::new(String::new());
22889        let class_unit = CodeUnit::new_fq(
22890            file.clone(),
22891            CodeUnitType::Class,
22892            "testing",
22893            "ScopedFakeTestPartResultReporter",
22894            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
22895        );
22896        let scope = ScopeInfo {
22897            package_name: "testing".to_string(),
22898            module: None,
22899            class_unit: Some(class_unit),
22900            template_signature: Some("<typename T>".to_string()),
22901            template_metadata: Some(CppTemplateMetadata {
22902                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
22903                primary_fq_name: String::new(),
22904                parameters: Vec::new(),
22905                specialization_arguments: Vec::new(),
22906                alias_target: None,
22907            }),
22908            declarations_are_fields: true,
22909            recovered_specialization_member_scope: true,
22910            visible_using_namespaces: Vec::new(),
22911            local_block: None,
22912            visible_using_declarations: Arc::default(),
22913        };
22914        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22915        let properties = RefCell::new(HashMap::default());
22916        let dependencies =
22917            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22918        let ancestry = ParentIndex::new(tree.root_node());
22919        let mut visitor = CppVisitor {
22920            file: &file,
22921            source,
22922            parsed: &mut recovered_parsed,
22923            source_collector: &collector,
22924            source_properties: &properties,
22925            source_dependencies: &dependencies,
22926            primary_ancestry: &ancestry,
22927            primary_root_id: tree.root_node().id(),
22928            source_links: HashMap::default(),
22929            source_declarations: HashSet::default(),
22930            preprocessor_context_cache: Default::default(),
22931            active_source_context: None,
22932            c_tag_semantics: false,
22933            c_tag_scope_witness: false,
22934            recovered_class_sibling_scopes: HashMap::default(),
22935            consumed_fragment_regions: Vec::new(),
22936            orphaned_namespaces: &index,
22937            partitioned_regions: Vec::new(),
22938            namespace_forward_scans: HashMap::default(),
22939            recovery_captures: Vec::new(),
22940            object_macro_fields: HashMap::default(),
22941            ambiguous_object_macro_fields: HashSet::default(),
22942        };
22943        let recovered = visitor
22944            .recovered_namespace_scope(tail_node, &scope)
22945            .expect("the tail must use the stolen namespace scope");
22946        assert_eq!(recovered.package_name, "testing::internal");
22947        assert!(
22948            recovered.class_unit.is_none(),
22949            "recovered namespace declarations cannot retain the malformed class owner"
22950        );
22951        assert!(recovered.template_signature.is_none());
22952        assert!(recovered.template_metadata.is_none());
22953        assert!(!recovered.declarations_are_fields);
22954        assert!(!recovered.recovered_specialization_member_scope);
22955        assert!(
22956            declarations
22957                .iter()
22958                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
22959            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
22960        );
22961        assert!(
22962            declarations
22963                .iter()
22964                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
22965            "the recovered free function remains in its namespace: {declarations:#?}"
22966        );
22967        assert!(
22968            declarations
22969                .iter()
22970                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
22971            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
22972        );
22973        assert!(
22974            declarations.iter().all(|unit| {
22975                !unit
22976                    .short_name()
22977                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
22978            }),
22979            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
22980        );
22981        assert!(
22982            declarations
22983                .iter()
22984                .all(|unit| !unit.identifier().is_empty()),
22985            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
22986        );
22987    }
22988
22989    /// #4292: a recovered namespace level whose close the brace stack could not
22990    /// prove keeps its head-only range, and the first declaration the walk
22991    /// reaches inside the region is not the namespace. The Module's name
22992    /// occurrence must be the level's own name token; deriving it from the
22993    /// walk's current node paired the head range with an unrelated name and
22994    /// panicked the collector's containment assertion.
22995    #[test]
22996    fn recovered_namespace_levels_name_their_own_name_tokens() {
22997        // #3084's collapsed-head fixture without the namespace's own close:
22998        // the file ends inside the namespace, so the brace stack proves no
22999        // close for its `{` and the level keeps its `namespace Name {` head
23000        // alone (#3328).
23001        let source = r#"namespace outer::app {
23002
23003    class Target {
23004        int value_;
23005    };
23006
23007    template <typename T>
23008    class Holder {
23009    public:
23010        explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
23011
23012#define HOLDER_DEFINE_OP( id, op )                                             \
23013    template <typename U>                                                      \
23014    constexpr friend auto operator op( Holder&& lhs, U&& rhs )                 \
23015        -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> {       \
23016        return Target{};                                                       \
23017    }
23018
23019        HOLDER_DEFINE_OP( equal, == )
23020#undef HOLDER_DEFINE_OP
23021        T m_lhs;
23022    };
23023
23024    class Tail {};
23025"#;
23026        let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
23027        let mut parser = tree_sitter::Parser::new();
23028        parser
23029            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23030            .unwrap();
23031        let tree = parser.parse(source, None).unwrap();
23032        assert!(
23033            tree.root_node().has_error(),
23034            "the fixture must reproduce the parse damage"
23035        );
23036        let target_start = source.find("class Target").expect("target class");
23037        let target_node = tree
23038            .root_node()
23039            .named_descendant_for_byte_range(target_start, target_start + "class".len())
23040            .expect("target class AST node");
23041        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
23042        // The macro-declaration grammar keeps the namespace head and its
23043        // members inside a surviving `namespace_definition`; the missing close
23044        // no longer collapses the head into an ERROR, so the scope authority
23045        // answers from parsed ancestry rather than a recovered region.
23046        assert_eq!(
23047            index.enclosing_namespace_components(target_node, source),
23048            ["outer", "app"]
23049        );
23050        let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
23051        let mut recovered_parsed = ParsedFile::new(String::new());
23052        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
23053        let properties = RefCell::new(HashMap::default());
23054        let dependencies =
23055            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
23056        let ancestry = ParentIndex::new(tree.root_node());
23057        let mut visitor = CppVisitor {
23058            file: &file,
23059            source,
23060            parsed: &mut recovered_parsed,
23061            source_collector: &collector,
23062            source_properties: &properties,
23063            source_dependencies: &dependencies,
23064            primary_ancestry: &ancestry,
23065            primary_root_id: tree.root_node().id(),
23066            source_links: HashMap::default(),
23067            source_declarations: HashSet::default(),
23068            preprocessor_context_cache: Default::default(),
23069            active_source_context: None,
23070            c_tag_semantics: false,
23071            c_tag_scope_witness: false,
23072            recovered_class_sibling_scopes: HashMap::default(),
23073            consumed_fragment_regions: Vec::new(),
23074            orphaned_namespaces: &index,
23075            partitioned_regions: Vec::new(),
23076            namespace_forward_scans: HashMap::default(),
23077            recovery_captures: Vec::new(),
23078            object_macro_fields: HashMap::default(),
23079            ambiguous_object_macro_fields: HashSet::default(),
23080        };
23081        // The surviving `namespace_definition` takes the ordinary namespace
23082        // visit, which declares each level with its own name token.
23083        visitor.visit_container(
23084            tree.root_node(),
23085            &ancestry,
23086            "",
23087            None,
23088            None,
23089            None,
23090            Vec::new(),
23091            Arc::default(),
23092        );
23093        let collector = collector.borrow();
23094        for (module_name, expected_name) in [
23095            (
23096                "outer",
23097                Range {
23098                    start_byte: 10,
23099                    end_byte: 15,
23100                    start_line: 1,
23101                    end_line: 1,
23102                },
23103            ),
23104            (
23105                "outer::app",
23106                Range {
23107                    start_byte: 17,
23108                    end_byte: 20,
23109                    start_line: 1,
23110                    end_line: 1,
23111                },
23112            ),
23113        ] {
23114            let declaration = visitor
23115                .source_links
23116                .iter()
23117                .find(|(unit, _)| unit.fq_name() == module_name)
23118                .expect("the recovered module links a source declaration")
23119                .1
23120                .last()
23121                .expect("the recovered module declaration");
23122            let declaration = collector.declaration(*declaration);
23123            let name = declaration
23124                .name
23125                .expect("the recovered module records its name occurrence");
23126            assert_eq!(
23127                collector.occurrence(name).range,
23128                expected_name,
23129                "the recovered module names its own token: {module_name}"
23130            );
23131        }
23132        assert!(
23133            parsed.declarations().iter().any(|unit| {
23134                unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
23135            }),
23136            "the recovered namespace members keep their namespace: {:#?}",
23137            parsed.declarations()
23138        );
23139    }
23140
23141    #[test]
23142    #[hardwon(
23143        "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23144    )]
23145    fn object_like_field_macros_materialize_owner_specific_declarations() {
23146        let source = r#"#define PUBLIC_FIELDS int public_value;
23147#define PRIVATE_FIELDS int private_value;
23148#define NOT_A_FIELD_LIST not a declaration
23149
23150struct First {
23151  PUBLIC_FIELDS
23152  PRIVATE_FIELDS
23153};
23154struct Second {
23155  PUBLIC_FIELDS
23156  NOT_A_FIELD_LIST
23157};
23158#undef PUBLIC_FIELDS
23159struct Third {
23160  PUBLIC_FIELDS
23161};
23162"#;
23163        let parsed = parse_cpp_declarations(source, "macro-fields.c");
23164        let fields = parsed
23165            .declarations()
23166            .iter()
23167            .filter(|unit| unit.is_field())
23168            .map(|unit| unit.fq_name())
23169            .collect::<Vec<_>>();
23170
23171        assert!(
23172            fields.contains(&"First.public_value".to_string()),
23173            "{fields:?}"
23174        );
23175        assert!(
23176            fields.contains(&"First.private_value".to_string()),
23177            "{fields:?}"
23178        );
23179        assert!(
23180            fields.contains(&"Second.public_value".to_string()),
23181            "{fields:?}"
23182        );
23183        assert!(
23184            !fields.iter().any(|field| field.contains("not_a_field")),
23185            "malformed macro must fail closed: {fields:?}"
23186        );
23187        assert!(
23188            !fields.iter().any(|field| field.starts_with("Third.")),
23189            "undefined macro must fail closed: {fields:?}"
23190        );
23191    }
23192
23193    #[test]
23194    #[hardwon(
23195        "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23196    )]
23197    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
23198        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
23199        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
23200        let mut macros = parsed
23201            .declarations()
23202            .iter()
23203            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
23204            .collect::<Vec<_>>();
23205        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
23206
23207        assert_eq!(macros.len(), 2, "{macros:#?}");
23208        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
23209        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
23210        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
23211        assert_eq!(
23212            parsed.declaration_ranges(macros[1])[0].start_byte,
23213            source.rfind("#define VALUE 2").expect("second definition")
23214        );
23215    }
23216
23217    #[test]
23218    #[hardwon(
23219        "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
23220    )]
23221    fn identifies_export_macro_class_base_displaced_into_declarator() {
23222        let source = r#"#define PROJECT_API_
23223namespace project {
23224namespace internal {
23225template <typename T>
23226class Base {};
23227}
23228template <typename T>
23229class Wrapper;
23230template <>
23231class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
23232}
23233"#;
23234        let mut parser = tree_sitter::Parser::new();
23235        parser
23236            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23237            .unwrap();
23238        let tree = parser.parse(source, None).unwrap();
23239        let start = source.find("internal::Base<int>").expect("base");
23240        let mut base = tree
23241            .root_node()
23242            .descendant_for_byte_range(start, start + 8)
23243            .expect("base syntax");
23244        while base.kind() != "qualified_identifier" {
23245            base = base.parent().expect("qualified base ancestor");
23246        }
23247        assert!(
23248            is_recovered_exported_class_base_type_node(base, source),
23249            "{}",
23250            tree.root_node().to_sexp()
23251        );
23252    }
23253
23254    fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
23255        parsed
23256            .declarations()
23257            .iter()
23258            .filter(|unit| unit.is_function())
23259            .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
23260            .collect()
23261    }
23262
23263    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
23264        let mut identities = all_function_identities(parsed)
23265            .into_iter()
23266            .map(|(name, signature)| (name, signature.unwrap_or_default()))
23267            .collect::<Vec<_>>();
23268        identities.sort();
23269        identities
23270    }
23271
23272    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
23273    /// prototype macros. Each case is parsed independently so a preceding
23274    /// malformed line cannot flatten the next declaration's fields into one
23275    /// cascading `ERROR`; that shape has no structural proof and must fail
23276    /// closed rather than fall back to a token scan. Parameter names are
23277    /// dropped from signatures just as they are for ordinary C prototypes.
23278    #[test]
23279    #[hardwon(
23280        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
23281    )]
23282    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
23283        let cases = [
23284            (
23285                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
23286                "pg_typemap_fit_to_result",
23287                "(VALUE, VALUE)",
23288            ),
23289            (
23290                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
23291                "pg_typemap_result_value",
23292                "(t_typemap *, VALUE, int, int)",
23293            ),
23294            (
23295                "void pg_typemap_mark _(( void * ));",
23296                "pg_typemap_mark",
23297                "(void *)",
23298            ),
23299            (
23300                "void init_pg_type_map _(( void ));",
23301                "init_pg_type_map",
23302                "(void)",
23303            ),
23304            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
23305            (
23306                "extern VALUE pg_extern _(( VALUE, VALUE ));",
23307                "pg_extern",
23308                "(VALUE, VALUE)",
23309            ),
23310            (
23311                "size_t pg_typemap_memsize _(( const void * ));",
23312                "pg_typemap_memsize",
23313                "(const void *)",
23314            ),
23315            (
23316                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
23317                "pg_wrap_socket_io",
23318                "(int, VALUE, VALUE *, int *)",
23319            ),
23320            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
23321            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
23322            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
23323        ];
23324
23325        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
23326            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
23327            assert_eq!(
23328                function_identities(&parsed),
23329                vec![(name.to_string(), signature.to_string())],
23330                "{source}: {:#?}",
23331                parsed.declarations()
23332            );
23333            assert!(
23334                parsed.declarations().iter().all(|unit| !unit.is_field()),
23335                "{source} must not retain the malformed field: {:#?}",
23336                parsed.declarations()
23337            );
23338        }
23339    }
23340
23341    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
23342    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
23343    /// it as a multiplication of a type by a call
23344    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
23345    /// wrapped in an `expression_statement` that `has_error()` only because
23346    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
23347    /// here with a clean preceding field so the prototype remains the
23348    /// parser-owned `expression_statement` this recovery requires.
23349    #[test]
23350    #[hardwon(
23351        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23352    )]
23353    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23354        let cases = [
23355            (
23356                "PGconn *pg_get_pgconn _(( VALUE ));",
23357                "pg_get_pgconn",
23358                "(VALUE)",
23359            ),
23360            (
23361                "PGresult* pgresult_get _(( VALUE ));",
23362                "pgresult_get",
23363                "(VALUE)",
23364            ),
23365        ];
23366        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23367            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23368            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23369            assert_eq!(
23370                function_identities(&parsed),
23371                vec![(name.to_string(), signature.to_string())],
23372                "{source}: {:#?}",
23373                parsed.declarations()
23374            );
23375            assert_eq!(
23376                parsed
23377                    .declarations()
23378                    .iter()
23379                    .filter(|unit| unit.is_field())
23380                    .map(|unit| unit.fq_name())
23381                    .collect::<Vec<_>>(),
23382                vec!["rb_mPG".to_string()],
23383                "{source}: {:#?}",
23384                parsed.declarations()
23385            );
23386        }
23387    }
23388
23389    /// #6902. A class-body prototype macro line is a `field_declaration`. It
23390    /// takes the same candidate gate and reparse as a file-scope one, inside
23391    /// the class envelope, so the member keeps its owner, its written range,
23392    /// its `static`, and the access its label states.
23393    #[test]
23394    #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23395    fn c_prototype_macro_recovers_class_body_members() {
23396        const SOURCE: &str = concat!(
23397            "class Pg {\n",
23398            "  VALUE wrap _(( VALUE self ));\n",
23399            "public:\n",
23400            "  int invoke __P((int value));\n",
23401            "  static char *name PROTO(( int index ));\n",
23402            "};\n",
23403        );
23404        let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23405        assert_eq!(
23406            function_identities(&parsed),
23407            vec![
23408                ("Pg.invoke".to_string(), "(int)".to_string()),
23409                ("Pg.name".to_string(), "(int)".to_string()),
23410                ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23411            ],
23412            "{:#?}",
23413            parsed.declarations()
23414        );
23415        assert!(
23416            parsed.declarations().iter().all(|unit| !unit.is_field()),
23417            "the malformed fields must not survive: {:#?}",
23418            parsed.declarations()
23419        );
23420        let class = parsed
23421            .declarations()
23422            .iter()
23423            .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23424            .expect("class Pg");
23425        let mut children = parsed
23426            .children
23427            .get(class)
23428            .into_iter()
23429            .flatten()
23430            .map(|unit| unit.fq_name())
23431            .collect::<Vec<_>>();
23432        children.sort();
23433        assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23434        for (name, written) in [
23435            ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23436            ("Pg.invoke", "int invoke __P((int value));"),
23437            ("Pg.name", "static char *name PROTO(( int index ));"),
23438        ] {
23439            let unit = parsed
23440                .declarations()
23441                .iter()
23442                .find(|unit| unit.is_function() && unit.fq_name() == name)
23443                .unwrap_or_else(|| panic!("missing recovered member {name}"));
23444            let [range] = parsed.declaration_ranges(unit) else {
23445                panic!("{name} must have exactly one range");
23446            };
23447            assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23448        }
23449        assert_eq!(
23450            recorded_callable_modifiers(&parsed),
23451            vec![
23452                "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23453                    .to_string(),
23454                "Pg.name decl_only=true static=true constructor=false visibility=Public"
23455                    .to_string(),
23456                "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23457                    .to_string(),
23458            ],
23459        );
23460    }
23461
23462    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
23463    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
23464    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
23465    /// regions. The witnessed declaration must still become a Function and
23466    /// its type must not survive as the name of a bogus Field. This test does
23467    /// not claim that structurally flattened neighboring prototypes are safe
23468    /// to recover.
23469    #[test]
23470    #[hardwon(
23471        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23472    )]
23473    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23474        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
23475VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
23476int pg_typemap_fit_to_copy_get                         _(( VALUE ));
23477VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
23478t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
23479VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
23480void pg_typemap_mark                                   _(( void * ));
23481size_t pg_typemap_memsize                              _(( const void * ));
23482void pg_typemap_compact                                _(( void * ));
23483
23484PGconn *pg_get_pgconn                                  _(( VALUE ));
23485t_pg_connection *pg_get_connection                     _(( VALUE ));
23486VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
23487#ifdef __GNUC__
23488__attribute__((format(printf, 3, 4)))
23489#endif
23490NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
23491VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23492void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23493
23494
23495VALUE pg_new_result                                    _(( PGresult *, VALUE ));
23496VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
23497PGresult* pgresult_get                                 _(( VALUE ));
23498VALUE pg_result_check                                  _(( VALUE ));
23499VALUE pg_result_clear                                  _(( VALUE ));
23500VALUE pg_tuple_new                                     _(( VALUE, int ));
23501
23502/*
23503 * Fetch the data pointer for the result object
23504 */
23505static inline t_pg_result *
23506pgresult_get_this( VALUE self )
23507{
23508	return RTYPEDDATA_DATA(self);
23509}
23510
23511
23512rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
23513const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
23514rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
23515
23516"#;
23517        let parsed = parse_cpp_declarations(source, "pg.h");
23518        assert!(
23519            function_identities(&parsed)
23520                .iter()
23521                .any(|(name, signature)| name == "pg_typemap_result_value"
23522                    && signature == "(t_typemap *, VALUE, int, int)"),
23523            "the real issue witness must be a Function with its C signature: {:#?}",
23524            parsed.declarations()
23525        );
23526        assert!(
23527            parsed
23528                .declarations()
23529                .iter()
23530                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23531            "the issue witness must not leave its return type as a Field name: {:#?}",
23532            parsed.declarations()
23533        );
23534    }
23535
23536    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
23537    /// between `explicit` and a constructor name onto the name, producing a
23538    /// `qualified_identifier` whose `::` it had to invent. The declared member
23539    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
23540    /// macro in type position, was already recovered and is the control that
23541    /// both spellings agree.
23542    #[test]
23543    #[hardwon(
23544        "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23545    )]
23546    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23547        let source = r#"class SIMD_4x26 final {
23548   public:
23549      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23550      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23551      int m_v;
23552};
23553"#;
23554        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23555        assert_eq!(
23556            function_identities(&parsed),
23557            vec![
23558                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
23559                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
23560            ],
23561            "{:#?}",
23562            parsed.declarations()
23563        );
23564    }
23565
23566    #[test]
23567    #[hardwon(
23568        "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
23569    )]
23570    fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
23571        let source = r#"struct cpu_spec {};
23572static struct cpu_spec __initdata cpu_specs[];
23573static struct cpu_spec ns::cpu_specs[];
23574"#;
23575        let tree = crate::diagnostics::parse_cpp_tree(source);
23576        let root = tree.root_node();
23577        let declarations = root
23578            .named_children(&mut root.walk())
23579            .filter(|node| node.kind() == "declaration")
23580            .collect::<Vec<_>>();
23581        let names = declarations
23582            .iter()
23583            .filter_map(|declaration| declaration.child_by_field_name("declarator"))
23584            .filter_map(|declarator| extract_variable_name(declarator, source))
23585            .collect::<Vec<_>>();
23586        assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
23587    }
23588
23589    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
23590    /// `ERROR` holding the wrapped declaration and every declaration after it
23591    /// until the parser recovers. All of them must be indexed, each with the
23592    /// byte range that spells it.
23593    #[test]
23594    #[hardwon(
23595        "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
23596    )]
23597    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
23598        let source = r#"#include <cstdint>
23599struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
23600    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
23601                     struct llama_model * model,
23602              struct llama_context_params   params),
23603            "use llama_init_from_model instead");
23604    LLAMA_API int32_t llama_tokenize(
23605        const struct llama_vocab * vocab,
23606                      const char * text,
23607                            bool   parse_special);
23608    LLAMA_API int32_t llama_other(int a);
23609"#;
23610        let parsed = parse_cpp_declarations(source, "llama.h");
23611        assert_eq!(
23612            function_identities(&parsed),
23613            vec![
23614                (
23615                    "llama_new_context_with_model".to_string(),
23616                    "(struct llama_model *, struct llama_context_params)".to_string()
23617                ),
23618                ("llama_other".to_string(), "(int)".to_string()),
23619                (
23620                    "llama_tokenize".to_string(),
23621                    "(const struct llama_vocab *, const char *, bool)".to_string()
23622                ),
23623            ],
23624            "{:#?}",
23625            parsed.declarations()
23626        );
23627
23628        // Each recovered declaration owns the source that spells it, so
23629        // navigation lands on the declaration and not on the macro envelope.
23630        for (name, expected) in [
23631            (
23632                "llama_new_context_with_model",
23633                "LLAMA_API struct llama_context * llama_new_context_with_model(",
23634            ),
23635            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
23636            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
23637        ] {
23638            let unit = parsed
23639                .declarations()
23640                .iter()
23641                .find(|unit| unit.is_function() && unit.fq_name() == name)
23642                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
23643            let [range] = parsed.declaration_ranges(unit) else {
23644                panic!("{name} must have exactly one range");
23645            };
23646            let text = &source[range.start_byte..range.end_byte];
23647            assert!(
23648                text.starts_with(expected),
23649                "{name} range is {text:?}, expected it to start with {expected:?}"
23650            );
23651            assert!(
23652                text.ends_with(')') || text.ends_with(';'),
23653                "{name}: {text:?}"
23654            );
23655        }
23656    }
23657
23658    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
23659    /// invocation's `(` at the end of its line. The macro-declaration grammar
23660    /// keeps each invocation in its own declaration-scope `ERROR`: the wrapped
23661    /// declaration and the hint's exploded words sit in the parameter list,
23662    /// the invocation's `)` closes that list, and its `;` is left to the
23663    /// sibling `expression_statement`. (Before the macro-declaration grammar,
23664    /// the second invocation's envelope swallowed the third into a run of bare
23665    /// siblings; #3094 read that flattening from the token order, and the
23666    /// collapsed-run recovery still owns the envelope/statement pairing the
23667    /// same way.)
23668    ///
23669    /// The identifiers are `library_*` at whisper's own lengths on purpose:
23670    /// tree-sitter prices error recovery partly by how much input a repair
23671    /// skips, and the same construct written with shorter names parses cleanly.
23672    #[test]
23673    #[hardwon(
23674        "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
23675    )]
23676    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
23677        let source = r#"    LIBRARY_DEPRECATED(
23678        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23679        "use library_init_from_file_with_params instead"
23680    );
23681    LIBRARY_DEPRECATED(
23682        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
23683        "use library_init_from_buffer_with_params instead"
23684    );
23685    LIBRARY_DEPRECATED(
23686        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
23687        "use library_init_with_params instead"
23688    );
23689"#;
23690        let mut parser = tree_sitter::Parser::new();
23691        parser
23692            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23693            .expect("C++ grammar");
23694        let tree = parser.parse(source, None).expect("C++ tree");
23695        let root = tree.root_node();
23696
23697        // The shape this recovery exists for: three sibling `ERROR` envelopes,
23698        // each holding one whole invocation, each followed by the statement
23699        // that owns the invocation's `;`.
23700        let mut cursor = root.walk();
23701        let items = root.children(&mut cursor).collect::<Vec<_>>();
23702        let [
23703            first,
23704            first_statement,
23705            second,
23706            second_statement,
23707            third,
23708            third_statement,
23709        ] = items.as_slice()
23710        else {
23711            panic!("{}", root.to_sexp());
23712        };
23713        let envelopes = [
23714            (first, first_statement),
23715            (second, second_statement),
23716            (third, third_statement),
23717        ];
23718        for (envelope, statement) in envelopes {
23719            assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
23720            assert_eq!(
23721                statement.kind(),
23722                "expression_statement",
23723                "{}",
23724                root.to_sexp()
23725            );
23726            assert!(
23727                node_text(*envelope, source).ends_with(')'),
23728                "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
23729                root.to_sexp()
23730            );
23731        }
23732
23733        // Each is admitted, and each names the byte just past its own `;`. A
23734        // self-contained invocation swallows nothing, so the recovery owns
23735        // only its own bytes.
23736        let ancestry = ParentIndex::new(root);
23737        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
23738            .expect("the first invocation");
23739        assert_eq!(
23740            &source[..first_run.invocation_end],
23741            &source[..source.find("instead\"\n    );").expect("first hint")
23742                + "instead\"\n    );".len()]
23743        );
23744        for (envelope, _) in envelopes {
23745            let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
23746                .expect("each invocation");
23747            assert_eq!(
23748                run.region_end, run.invocation_end,
23749                "a self-contained invocation owns only its own bytes"
23750            );
23751        }
23752
23753        let parsed = parse_cpp_declarations(source, "library.h");
23754        assert_eq!(
23755            function_identities(&parsed),
23756            vec![
23757                (
23758                    "library_init".to_string(),
23759                    "(struct library_model_loader *)".to_string()
23760                ),
23761                (
23762                    "library_init_from_buffer".to_string(),
23763                    "(void *, size_t)".to_string()
23764                ),
23765                (
23766                    "library_init_from_file".to_string(),
23767                    "(const char *)".to_string()
23768                ),
23769            ],
23770            "{:#?}",
23771            parsed.declarations()
23772        );
23773    }
23774
23775    #[test]
23776    #[hardwon(
23777        "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
23778    )]
23779    fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
23780        let source = r#"void host() {
23781#ifdef ENABLED
23782LIBRARY_DEPRECATED(
23783    int hidden(const char* value),
23784    "hint"
23785);
23786#endif
23787}
23788"#;
23789        let mut parser = tree_sitter::Parser::new();
23790        parser
23791            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23792            .expect("C++ grammar");
23793        let tree = parser.parse(source, None).expect("C++ tree");
23794        let root = tree.root_node();
23795        let function = root.named_child(0).expect("ordinary function");
23796        let body = function
23797            .child_by_field_name("body")
23798            .expect("ordinary function body");
23799        let conditional = body.named_child(0).expect("conditional body");
23800        let mut conditional_cursor = conditional.walk();
23801        let envelope = conditional
23802            .named_children(&mut conditional_cursor)
23803            .find(|child| child.kind() == "ERROR")
23804            .expect("macro error");
23805        assert_eq!(conditional.kind(), "preproc_ifdef");
23806        assert_eq!(envelope.kind(), "ERROR");
23807
23808        let ancestry = ParentIndex::new(root);
23809        assert!(declaration_scope_container(envelope, &ancestry).is_none());
23810        assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
23811
23812        let parsed = parse_cpp_declarations(source, "function-body-macro.h");
23813        assert!(
23814            parsed
23815                .declarations()
23816                .iter()
23817                .all(|unit| unit.fq_name() != "hidden"),
23818            "a function-body macro error must not become a global declaration: {:#?}",
23819            parsed.declarations()
23820        );
23821    }
23822
23823    /// The hand-off that keeps the scan finite. An invocation that reaches the
23824    /// end of its own declaration scope swallowed nothing, and the ordinary
23825    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
23826    /// that region for every invocation it finds, so admitting it here would
23827    /// hand the scan its own input back.
23828    #[test]
23829    #[hardwon(
23830        "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
23831    )]
23832    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
23833        let source = r#"LIBRARY_DEPRECATED(
23834        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23835        "use library_init_from_file_with_params instead"
23836    );"#;
23837        let mut parser = tree_sitter::Parser::new();
23838        parser
23839            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23840            .expect("C++ grammar");
23841        let tree = parser.parse(source, None).expect("C++ tree");
23842        let root = tree.root_node();
23843        let head = root.named_child(0).expect("the invocation");
23844        let ancestry = ParentIndex::new(root);
23845        assert!(
23846            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
23847            "{}",
23848            root.to_sexp()
23849        );
23850        assert!(
23851            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
23852            "the ordinary reader must be the one that has it: {}",
23853            root.to_sexp()
23854        );
23855        // The index is a speed substitution, never a semantic one: the
23856        // unindexed path is `Node::parent` itself and must agree.
23857        let unindexed = ParentIndex::unindexed();
23858        assert!(
23859            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
23860            "indexed and unindexed climbs must agree"
23861        );
23862        assert_eq!(
23863            macro_wrapped_declarations(head, source, &ancestry).len(),
23864            macro_wrapped_declarations(head, source, &unindexed).len(),
23865            "indexed and unindexed climbs must agree"
23866        );
23867    }
23868
23869    /// Negative controls for the same recovery: a macro invocation whose
23870    /// arguments are not a declaration recovers nothing, whether the parser
23871    /// keeps it clean, reads the arguments as a type, or reads them as a bare
23872    /// declarator.
23873    #[test]
23874    #[hardwon(
23875        "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
23876    )]
23877    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
23878        for source in [
23879            "int before;\nFOO(1, 2);\nint after;\n",
23880            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
23881            "int before;\nMACRO(int a, int b);\nint after;\n",
23882            "DECLARE_HANDLE(HWND);\nint after;\n",
23883        ] {
23884            let parsed = parse_cpp_declarations(source, "macro-call.h");
23885            assert_eq!(
23886                function_identities(&parsed),
23887                Vec::new(),
23888                "{source:?} must declare no function: {:#?}",
23889                parsed.declarations()
23890            );
23891        }
23892    }
23893
23894    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
23895    /// makes tree-sitter read the macro as the member's type and its argument
23896    /// list as a parenthesized declarator, which then swallows the attributed
23897    /// member and the member written after it. Both are members.
23898    #[test]
23899    #[hardwon(
23900        "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
23901    )]
23902    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
23903        let source = r#"#include <string_view>
23904namespace Botan {
23905class DL_Group final {
23906   public:
23907      DL_Group() = default;
23908      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23909      DL_Group(std::string_view pem, int format);
23910      size_t get_p() const;
23911};
23912}
23913"#;
23914        let parsed = parse_cpp_declarations(source, "dl_group.h");
23915        assert_eq!(
23916            function_identities(&parsed),
23917            vec![
23918                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
23919                (
23920                    "Botan.DL_Group.DL_Group".to_string(),
23921                    "(std::string_view)".to_string()
23922                ),
23923                (
23924                    "Botan.DL_Group.DL_Group".to_string(),
23925                    "(std::string_view, int)".to_string()
23926                ),
23927                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23928            ],
23929            "{:#?}",
23930            parsed.declarations()
23931        );
23932    }
23933
23934    /// #2552 shape 3, export-macro class. The class head macro makes the body a
23935    /// `compound_statement`, so members come from the region reparse, and one
23936    /// string-attribute member used to make that reparse unindexable -- which
23937    /// left the class with no members at all.
23938    #[test]
23939    #[hardwon(
23940        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
23941    )]
23942    fn an_export_macro_class_keeps_its_string_attribute_members() {
23943        let source = r#"#include <string_view>
23944namespace Botan {
23945class BOTAN_PUBLIC_API(2, 0) DL_Group final {
23946   public:
23947      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23948      DL_Group(std::string_view pem, int format);
23949      size_t get_p() const;
23950};
23951}
23952"#;
23953        let parsed = parse_cpp_declarations(source, "dl_group.h");
23954        assert!(
23955            parsed
23956                .declarations()
23957                .iter()
23958                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
23959            "{:#?}",
23960            parsed.declarations()
23961        );
23962        assert_eq!(
23963            function_identities(&parsed),
23964            vec![
23965                (
23966                    "Botan.DL_Group.DL_Group".to_string(),
23967                    "(std::string_view)".to_string()
23968                ),
23969                (
23970                    "Botan.DL_Group.DL_Group".to_string(),
23971                    "(std::string_view, int)".to_string()
23972                ),
23973                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23974            ],
23975            "{:#?}",
23976            parsed.declarations()
23977        );
23978    }
23979
23980    /// #2552 shape 3, the `= default` variant plus the access-label
23981    /// constructor. The attributed defaulted constructor separates cleanly, but
23982    /// it strands the next member in a bare `ERROR`, and a constructor written
23983    /// with a member-initializer list under `private:` dissolves into
23984    /// expression soup that the reparse recovers from its own source range.
23985    #[test]
23986    #[hardwon(
23987        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
23988    )]
23989    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
23990        let source = r#"namespace Botan {
23991class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
23992   public:
23993      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
23994      XMSS_Parameters(int oid, int len);
23995      size_t len() const;
23996
23997   private:
23998      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
23999            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
24000
24001      int m_oid;
24002      int m_wots_oid;
24003      size_t m_element_size;
24004      size_t m_tree_height;
24005};
24006}
24007"#;
24008        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
24009        let constructors = function_identities(&parsed)
24010            .into_iter()
24011            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
24012            .map(|(_, signature)| signature)
24013            .collect::<Vec<_>>();
24014        assert_eq!(
24015            constructors,
24016            vec![
24017                "()".to_string(),
24018                "(int, int)".to_string(),
24019                "(int, int, size_t, size_t)".to_string(),
24020            ],
24021            "{:#?}",
24022            parsed.declarations()
24023        );
24024    }
24025
24026    /// Negative control for the same rule: a real qualified name spells its
24027    /// `::` in the source, so the separator is present rather than MISSING and
24028    /// the out-of-line definition keeps its owner.
24029    #[test]
24030    #[hardwon(
24031        "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
24032    )]
24033    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
24034        let source = r#"namespace shell {
24035struct Outer {
24036   struct Inner {
24037      Inner(int v);
24038      void run(int v);
24039   };
24040};
24041Outer::Inner::Inner(int v) {}
24042void Outer::Inner::run(int v) {}
24043}
24044"#;
24045        let parsed = parse_cpp_declarations(source, "outer.cpp");
24046        let names = function_identities(&parsed)
24047            .into_iter()
24048            .map(|(fq_name, _)| fq_name)
24049            .collect::<Vec<_>>();
24050        assert!(
24051            names
24052                .iter()
24053                .all(|name| name.starts_with("shell.Outer$Inner.")),
24054            "{names:#?}"
24055        );
24056    }
24057
24058    #[test]
24059    #[hardwon(
24060        "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
24061    )]
24062    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
24063        let source = r#"namespace control {
24064template <typename T>
24065class AnySpan;
24066template <typename T>
24067class ABSL_ATTRIBUTE_VIEW AnySpan {
24068 public:
24069  int begin() const;
24070};
24071}
24072
24073namespace absl {
24074ABSL_NAMESPACE_BEGIN
24075template <typename T>
24076class ABSL_ATTRIBUTE_VIEW Span {
24077 public:
24078  int begin() const;
24079  int back() const;
24080};
24081
24082int begin();
24083int back();
24084}
24085"#;
24086        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
24087        let declarations = parsed.declarations();
24088        assert!(
24089            declarations
24090                .iter()
24091                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
24092        );
24093        for method in ["begin", "back"] {
24094            assert!(declarations.iter().any(|unit| {
24095                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
24096            }));
24097            assert!(
24098                declarations.iter().any(|unit| {
24099                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
24100                })
24101            );
24102        }
24103        assert!(
24104            declarations
24105                .iter()
24106                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
24107        );
24108        assert!(
24109            declarations
24110                .iter()
24111                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
24112        );
24113        assert!(
24114            declarations
24115                .iter()
24116                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
24117        );
24118    }
24119
24120    #[test]
24121    #[hardwon(
24122        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24123    )]
24124    fn explicit_global_member_definition_has_canonical_package_boundary() {
24125        let source = r#"
24126namespace arangodb::aql {
24127class ExecutionPlan {
24128 public:
24129  template<class... Args> Node* createNode(Args&&... args);
24130};
24131}
24132
24133template<class... Args>
24134Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
24135"#;
24136        let parsed = parse_cpp_declarations(source, "global-member.cpp");
24137
24138        assert!(parsed.declarations().iter().any(|unit| {
24139            unit.is_function()
24140                && unit.package_name() == "arangodb::aql"
24141                && unit.short_name() == "ExecutionPlan.createNode"
24142                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
24143        }));
24144    }
24145
24146    #[test]
24147    #[hardwon(
24148        "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24149    )]
24150    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
24151        let source = r#"
24152#ifndef TINYXML2_INCLUDED
24153#define TINYXML2_INCLUDED
24154namespace tinyxml2 {
24155class TINYXML2_LIB XMLUtil {
24156 public:
24157  static const char* SkipWhiteSpace(const char* p) {
24158    while (*p) {
24159      if (*p == ' ') {
24160        ++p;
24161      }
24162    }
24163    return p;
24164  }
24165  static bool StringEqual(const char* p, const char* q) {
24166    return p == q;
24167  }
24168  class TINYXML2_LIB Helper {
24169   public:
24170    void Touch();
24171  };
24172  static void ToStr(int value, char* buffer);
24173 private:
24174  static const char* writeBoolTrue;
24175};
24176
24177class TINYXML2_LIB XMLNode {
24178 public:
24179  virtual XMLNode* ShallowClone() const = 0;
24180  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
24181};
24182}
24183#endif
24184"#;
24185        let mut parser = tree_sitter::Parser::new();
24186        parser
24187            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24188            .unwrap();
24189        let tree = parser.parse(source, None).unwrap();
24190        let mut boundary_found = false;
24191        walk_named_tree_preorder(tree.root_node(), true, |node| {
24192            if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
24193                && name == "XMLUtil"
24194            {
24195                boundary_found = fragmented_export_sibling_class_boundary(
24196                    node,
24197                    source,
24198                    &ParentIndex::unindexed(),
24199                )
24200                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
24201                .is_some_and(|(_, name, _, _)| name == "XMLNode");
24202            }
24203            WalkControl::Continue
24204        });
24205        assert!(
24206            boundary_found,
24207            "fixture must exercise the recovered sibling boundary"
24208        );
24209
24210        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
24211        assert!(
24212            parsed
24213                .declarations()
24214                .iter()
24215                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
24216            "{:#?}",
24217            parsed.declarations()
24218        );
24219        assert!(
24220            parsed
24221                .declarations()
24222                .iter()
24223                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
24224            "{:#?}",
24225            parsed.declarations()
24226        );
24227        assert!(parsed.declarations().iter().any(|unit| {
24228            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
24229        }));
24230        assert!(
24231            parsed
24232                .declarations()
24233                .iter()
24234                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
24235        );
24236        assert!(
24237            parsed
24238                .declarations()
24239                .iter()
24240                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
24241        );
24242    }
24243
24244    #[test]
24245    #[hardwon(
24246        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24247    )]
24248    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
24249        // Clang's diagnostic suite intentionally contains this ill-formed
24250        // spelling. The analyzer must retain the parser's explicit-global AST
24251        // boundary instead of constructing `cwg311::::cwg311::X`.
24252        let parsed = parse_cpp_declarations(
24253            r#"
24254namespace cwg311 {
24255namespace X { namespace Y {} }
24256namespace ::cwg311::X {}
24257}
24258"#,
24259            "explicit-global-namespace.cpp",
24260        );
24261
24262        assert!(parsed.declarations().iter().any(|unit| {
24263            unit.kind() == CodeUnitType::Module
24264                && unit.short_name() == "cwg311::X"
24265                && unit.fq_name() == "cwg311::X"
24266        }));
24267        assert!(
24268            parsed
24269                .declarations()
24270                .iter()
24271                .all(|unit| !unit.short_name().contains("::::")),
24272            "recovered namespace names must not retain empty scope components: {:#?}",
24273            parsed.declarations()
24274        );
24275    }
24276
24277    #[test]
24278    #[hardwon(
24279        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24280    )]
24281    fn repeated_scope_separator_does_not_create_empty_function_owner() {
24282        let scope = ScopeInfo {
24283            package_name: "X".to_string(),
24284            module: None,
24285            class_unit: None,
24286            template_signature: None,
24287            template_metadata: None,
24288            declarations_are_fields: false,
24289            recovered_specialization_member_scope: false,
24290            visible_using_namespaces: Vec::new(),
24291            local_block: None,
24292            visible_using_declarations: Arc::default(),
24293        };
24294
24295        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
24296
24297        assert!(owner.is_none());
24298        assert_eq!(name, "doit");
24299        assert_eq!(package, "X");
24300    }
24301
24302    #[test]
24303    #[hardwon(
24304        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24305    )]
24306    fn trailing_decltype_expression_is_not_a_function_declarator() {
24307        let source = r#"
24308namespace boost { namespace detail {
24309#if ! defined(BOOST_NO_SFINAE_EXPR) && \
24310    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
24311    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
24312#define BOOST_THREAD_PROVIDES_INVOKE
24313#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
24314template <class Fp, class A0, class ...Args>
24315inline auto
24316invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
24317       BOOST_THREAD_RV_REF(Args) ...args)
24318    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
24319{
24320    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
24321}
24322#endif
24323#endif
24324}}
24325"#;
24326        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
24327
24328        assert!(
24329            parsed
24330                .declarations()
24331                .iter()
24332                .all(|unit| unit.short_name() != ".*f")
24333        );
24334    }
24335
24336    fn find_class_named<'tree>(
24337        root: Node<'tree>,
24338        source: &str,
24339        expected_name: &str,
24340    ) -> Option<Node<'tree>> {
24341        let mut stack = vec![root];
24342        while let Some(node) = stack.pop() {
24343            if node.kind() == "class_specifier"
24344                && node
24345                    .child_by_field_name("name")
24346                    .is_some_and(|name| node_text(name, source) == expected_name)
24347            {
24348                return Some(node);
24349            }
24350            let mut cursor = node.walk();
24351            stack.extend(node.named_children(&mut cursor));
24352        }
24353        None
24354    }
24355
24356    #[test]
24357    #[hardwon(
24358        "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24359    )]
24360    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24361        let source = r#"EXPORT void definition(struct Value value) {}
24362EXPORT void prototype(struct Value value);
24363"#;
24364        let mut parser = tree_sitter::Parser::new();
24365        parser
24366            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24367            .unwrap();
24368        let tree = parser.parse(source, None).unwrap();
24369        let root = tree.root_node();
24370        let mut cursor = root.walk();
24371        let callables = root
24372            .named_children(&mut cursor)
24373            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24374            .collect::<Vec<_>>();
24375
24376        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24377        for callable in callables {
24378            // The macro-declaration grammar parses these cleanly, with the
24379            // export macro retained as a `macro_modifier`; the preserved
24380            // callable and its `struct` parameter are what the sentinel gate
24381            // rejects on, with or without an ERROR in the envelope.
24382            assert!(
24383                cpp_sentinel_macro_parts(callable, source).is_none(),
24384                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24385            );
24386        }
24387    }
24388
24389    #[test]
24390    #[hardwon(
24391        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24392    )]
24393    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24394        let source = r#"namespace absl {
24395ABSL_NAMESPACE_BEGIN
24396// Generate a floating-point variate conforming to a Beta distribution:
24397template <typename RealType = double>
24398class beta_distribution {
24399 public:
24400  using result_type = RealType;
24401
24402
24403  beta_distribution() : beta_distribution(1) {}
24404
24405  explicit beta_distribution(result_type alpha, result_type beta = 1)
24406      : param_(alpha, beta) {}
24407
24408  explicit beta_distribution(const param_type& p) : param_(p) {}
24409
24410  void reset() {}
24411
24412  // Generating functions
24413  template <typename URBG>
24414  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
24415    return (*this)(g, param_);
24416  }
24417
24418};
24419ABSL_NAMESPACE_END
24420}  // namespace absl
24421"#;
24422        let mut parser = tree_sitter::Parser::new();
24423        parser
24424            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24425            .unwrap();
24426        let tree = parser.parse(source, None).unwrap();
24427        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24428        let body = namespace
24429            .child_by_field_name("body")
24430            .expect("fixture namespace body");
24431        let sentinel = body.named_child(0).expect("sentinel envelope");
24432
24433        // The macro-declaration grammar keeps the sentinel head in an `ERROR`
24434        // envelope that ends at the class name, with the class body as the
24435        // sibling it recovered with; the bogus callable the older grammar
24436        // assembled from the last member is no longer part of the envelope.
24437        assert_eq!(sentinel.kind(), "ERROR");
24438        assert!(
24439            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24440            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24441        );
24442
24443        let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24444        for expected in [
24445            "absl.beta_distribution",
24446            "absl.beta_distribution.beta_distribution",
24447            "absl.beta_distribution.reset",
24448        ] {
24449            assert!(
24450                parsed
24451                    .declarations()
24452                    .iter()
24453                    .any(|unit| unit.fq_name() == expected),
24454                "missing recovered declaration {expected}: {:#?}",
24455                parsed.declarations()
24456            );
24457        }
24458    }
24459
24460    #[test]
24461    #[hardwon(
24462        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24463    )]
24464    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24465        let source = r#"namespace absl {
24466ABSL_NAMESPACE_BEGIN
24467// absl::discrete_distribution
24468//
24469// A discrete distribution produces random integers i, where 0 <= i < n
24470template <typename IntType = int>
24471class discrete_distribution {
24472 public:
24473  using result_type = IntType;
24474  class param_type {
24475   public:
24476    param_type() { init(); }
24477    template <typename InputIterator>
24478    explicit param_type(InputIterator begin, InputIterator end)
24479        : p_(begin, end) {
24480      init();
24481    }
24482  };
24483  discrete_distribution() : param_() {}
24484  explicit discrete_distribution(const param_type& p) : param_(p) {}
24485};
24486ABSL_NAMESPACE_END
24487}  // namespace absl
24488"#;
24489        let mut parser = tree_sitter::Parser::new();
24490        parser
24491            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24492            .unwrap();
24493        let tree = parser.parse(source, None).unwrap();
24494        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24495        let body = namespace
24496            .child_by_field_name("body")
24497            .expect("fixture namespace body");
24498        let sentinel = body.named_child(0).expect("sentinel envelope");
24499        let callable = sentinel
24500            .child_by_field_name("declarator")
24501            .and_then(extract_function_declarator)
24502            .and_then(cpp_function_declarator_name_node)
24503            .expect("preserved callable name");
24504
24505        assert_eq!(sentinel.kind(), "function_definition");
24506        assert_eq!(callable.kind(), "identifier");
24507        assert!(
24508            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24509            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24510        );
24511    }
24512
24513    #[test]
24514    #[hardwon(
24515        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24516    )]
24517    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24518        let source = r#"
24519#define CPPCHECKLIB
24520class Library {
24521    struct Container {
24522        CPPCHECKLIB static std::string toString(Yield yield);
24523        CPPCHECKLIB static std::string toString(Action action);
24524    };
24525};
24526"#;
24527        let mut parser = tree_sitter::Parser::new();
24528        parser
24529            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24530            .unwrap();
24531        let tree = parser.parse(source, None).unwrap();
24532        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24533        let parsed = parse_cpp_file(&file, source, &tree);
24534        assert!(
24535            parsed
24536                .declarations()
24537                .iter()
24538                .all(|unit| unit.fq_name() != "Library$Container.std"),
24539            "the qualified return-type namespace must not become a field: {:#?}",
24540            parsed.declarations()
24541        );
24542        for expected in ["(Yield)", "(Action)"] {
24543            assert!(
24544                parsed.declarations().iter().any(|unit| {
24545                    unit.is_function()
24546                        && unit.fq_name() == "Library$Container.toString"
24547                        && unit.signature() == Some(expected)
24548                }),
24549                "recovered toString overload {expected} is missing: {:#?}",
24550                parsed.declarations()
24551            );
24552        }
24553    }
24554
24555    #[test]
24556    #[hardwon(
24557        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24558    )]
24559    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
24560        let source = r#"
24561#define SIMPLECPP_LIB
24562namespace simplecpp {
24563using TokenString = std::string;
24564struct Location { int line{}; };
24565class SIMPLECPP_LIB Token {
24566  TokenString prefix;
24567  void prefix_method() {}
24568 public:
24569  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24570      whitespaceahead(wsahead), location(loc), string(s)
24571      // The comment must not hide the constructor body from recovery.
24572      {
24573      flags();
24574  }
24575  TokenString string;
24576  bool whitespaceahead;
24577  Location location;
24578  Token *previous{};
24579 private:
24580  void flags() {
24581      whitespaceahead = true;
24582  }
24583};
24584}
24585"#;
24586        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
24587
24588        let location_fields = parsed
24589            .declarations()
24590            .iter()
24591            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
24592            .collect::<Vec<_>>();
24593        assert_eq!(
24594            location_fields.len(),
24595            1,
24596            "location should have one class-owned declaration: {:#?}",
24597            parsed.declarations()
24598        );
24599        assert!(
24600            location_fields[0].is_field(),
24601            "location has wrong kind: {:#?}",
24602            parsed.declarations()
24603        );
24604        assert!(
24605            parsed.declarations().iter().all(|unit| {
24606                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
24607            })
24608        );
24609        assert!(
24610            parsed.declarations().iter().all(|unit| {
24611                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24612            })
24613        );
24614        assert!(
24615            parsed
24616                .declarations()
24617                .iter()
24618                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
24619        );
24620        assert!(
24621            parsed
24622                .declarations()
24623                .iter()
24624                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
24625            "the recovered class must retain its constructor: {:#?}",
24626            parsed.declarations()
24627        );
24628        assert!(
24629            parsed
24630                .declarations()
24631                .iter()
24632                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
24633        );
24634        assert!(parsed.declarations().iter().any(|unit| {
24635            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
24636        }));
24637        let constructor = parsed
24638            .declarations()
24639            .iter()
24640            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
24641            .expect("recovered constructor");
24642        let constructor_start = source.find("Token(const").expect("constructor start");
24643        let constructor_end = source
24644            .get(
24645                ..source
24646                    .find("  TokenString string;")
24647                    .expect("constructor end"),
24648            )
24649            .expect("constructor slice")
24650            .trim_end()
24651            .len();
24652        assert!(
24653            parsed
24654                .navigation_ranges
24655                .get(constructor)
24656                .is_some_and(|ranges| {
24657                    ranges.iter().any(|range| {
24658                        range.start_byte == constructor_start && range.end_byte == constructor_end
24659                    })
24660                }),
24661            "constructor navigation must span the full body: {:#?}",
24662            parsed.navigation_ranges
24663        );
24664        assert_eq!(
24665            parsed
24666                .signature_metadata
24667                .get(constructor)
24668                .and_then(|metadata| metadata.first())
24669                .and_then(SignatureMetadata::callable_linkage),
24670            Some(CallableLinkage::External)
24671        );
24672        let token_class = parsed
24673            .declarations()
24674            .iter()
24675            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
24676            .expect("recovered Token class");
24677        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
24678        assert!(
24679            parsed
24680                .navigation_ranges
24681                .get(token_class)
24682                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
24683            "class navigation must include the terminating semicolon: {:#?}",
24684            parsed.navigation_ranges
24685        );
24686    }
24687
24688    #[test]
24689    #[hardwon(
24690        "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24691    )]
24692    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
24693        let source = r#"
24694#define SIMPLECPP_LIB
24695namespace simplecpp {
24696using TokenString = std::string;
24697class Macro;
24698struct Location {
24699  unsigned int fileIndex{};
24700  unsigned int line{};
24701  unsigned int col{};
24702};
24703struct Output {
24704  int type;
24705};
24706class SIMPLECPP_LIB Token {
24707 public:
24708  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24709      whitespaceahead(wsahead), location(loc), string(s) {
24710      flags();
24711  }
24712  Token(const Token &tok) :
24713      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
24714      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
24715      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
24716  Token &operator=(const Token &tok) = delete;
24717  const TokenString& str() const { return string; }
24718  void setstr(const std::string &s) { string = s; flags(); }
24719  bool isOneOf(const char ops[]) const;
24720  TokenString macro;
24721  char op;
24722  bool comment;
24723  bool name;
24724  bool number;
24725  bool whitespaceahead;
24726  Location location;
24727  Token *previous{};
24728  Token *next{};
24729 private:
24730  void flags() {
24731      name = !string.empty();
24732      comment = false;
24733      number = false;
24734      op = 0;
24735  }
24736  TokenString string;
24737};
24738}
24739struct Following {
24740  int type;
24741};
24742class SIMPLECPP_LIB Later {
24743 public:
24744  Later(int value) : value(value) {}
24745  int value;
24746};
24747"#;
24748        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
24749        assert!(
24750            parsed
24751                .declarations()
24752                .iter()
24753                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
24754        );
24755        assert!(
24756            !parsed
24757                .declarations()
24758                .iter()
24759                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
24760        );
24761        assert!(
24762            parsed
24763                .declarations()
24764                .iter()
24765                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
24766        );
24767        assert!(
24768            !parsed
24769                .declarations()
24770                .iter()
24771                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24772        );
24773        assert!(
24774            parsed
24775                .declarations()
24776                .iter()
24777                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
24778        );
24779        assert!(
24780            parsed
24781                .declarations()
24782                .iter()
24783                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
24784        );
24785        assert!(
24786            parsed
24787                .declarations()
24788                .iter()
24789                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
24790        );
24791        assert!(
24792            parsed
24793                .declarations()
24794                .iter()
24795                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
24796        );
24797        assert!(
24798            parsed
24799                .declarations()
24800                .iter()
24801                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
24802        );
24803        assert!(
24804            parsed
24805                .declarations()
24806                .iter()
24807                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
24808        );
24809        assert!(parsed.declarations().iter().all(|unit| {
24810            !matches!(
24811                unit.fq_name().as_str(),
24812                "simplecpp.Token.Following" | "simplecpp.Token.Later"
24813            )
24814        }));
24815        assert!(
24816            !parsed
24817                .declarations()
24818                .iter()
24819                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
24820            "the following struct must remain outside the recovered Token class"
24821        );
24822    }
24823
24824    #[test]
24825    #[hardwon(
24826        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24827    )]
24828    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
24829        let source = r#"
24830#define SIMPLECPP_LIB
24831namespace {
24832namespace simplecpp {
24833using TokenString = std::string;
24834struct Location { int line{}; };
24835class SIMPLECPP_LIB HiddenToken {
24836 public:
24837  HiddenToken(const TokenString &s, const Location &loc) :
24838      location(loc), string(s) {
24839      flags();
24840  }
24841  TokenString string;
24842  Location location;
24843  HiddenToken *previous{};
24844 private:
24845  void flags() {}
24846};
24847}
24848}
24849"#;
24850        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
24851        let constructor = parsed
24852            .declarations()
24853            .iter()
24854            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
24855            .expect("recovered anonymous-namespace constructor");
24856        assert_eq!(
24857            parsed
24858                .signature_metadata
24859                .get(constructor)
24860                .and_then(|metadata| metadata.first())
24861                .and_then(SignatureMetadata::callable_linkage),
24862            Some(CallableLinkage::Internal)
24863        );
24864    }
24865
24866    #[test]
24867    #[hardwon(
24868        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24869    )]
24870    fn macro_qualified_static_field_keeps_real_declarator() {
24871        let source = r#"#define JSON_INLINE_VARIABLE
24872struct Reader {
24873static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
24874static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
24875static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
24876};"#;
24877        let mut parser = tree_sitter::Parser::new();
24878        parser
24879            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24880            .unwrap();
24881        let tree = parser.parse(source, None).unwrap();
24882        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
24883        let parsed = parse_cpp_file(&file, source, &tree);
24884        for expected in [
24885            "Reader.npos",
24886            "Reader.other",
24887            "Reader.pointer",
24888            "Reader.reference",
24889        ] {
24890            assert!(
24891                parsed
24892                    .declarations()
24893                    .iter()
24894                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
24895                "real macro-decorated field {expected} is missing: {:#?}",
24896                parsed.declarations()
24897            );
24898        }
24899        assert!(
24900            parsed
24901                .declarations()
24902                .iter()
24903                .all(|unit| unit.fq_name() != "Reader.std"),
24904            "qualified type prefix became a pseudo-field: {:#?}",
24905            parsed.declarations()
24906        );
24907        let root = tree.root_node();
24908        let mut stack = vec![root];
24909        let mut signatures = Vec::new();
24910        // The macro-declaration grammar keeps `JSON_INLINE_VARIABLE` as a
24911        // `macro_modifier`, so the fields arrive as ordinary clean field
24912        // declarations whose declarators carry the signature text the
24913        // bitfield-clause recovery used to reconstruct.
24914        while let Some(current) = stack.pop() {
24915            if current.kind() == "field_declaration" {
24916                let mut cursor = current.walk();
24917                for declarator in current.children_by_field_name("declarator", &mut cursor) {
24918                    signatures.push(render_cpp_field_signature(current, declarator, source));
24919                }
24920            }
24921            let mut cursor = current.walk();
24922            stack.extend(current.named_children(&mut cursor));
24923        }
24924        signatures.sort();
24925        assert_eq!(
24926            signatures,
24927            [
24928                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
24929                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
24930                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
24931                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
24932            ]
24933        );
24934    }
24935
24936    fn member_function_linkage(source: &str) -> CallableLinkage {
24937        let mut parser = tree_sitter::Parser::new();
24938        parser
24939            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24940            .unwrap();
24941        let tree = parser.parse(source, None).unwrap();
24942        let ancestry = ParentIndex::new(tree.root_node());
24943        let mut stack = vec![tree.root_node()];
24944        while let Some(node) = stack.pop() {
24945            if node.kind() == "function_definition" {
24946                let mut current = node.parent();
24947                while let Some(parent) = current {
24948                    if matches!(
24949                        parent.kind(),
24950                        "class_specifier" | "struct_specifier" | "union_specifier"
24951                    ) {
24952                        return cpp_callable_linkage(node, source, &ancestry);
24953                    }
24954                    current = parent.parent();
24955                }
24956            }
24957            let mut cursor = node.walk();
24958            stack.extend(node.named_children(&mut cursor));
24959        }
24960        panic!("fixture has no member function definition");
24961    }
24962
24963    #[test]
24964    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
24965        assert_eq!(
24966            member_function_linkage("struct Named { int method() { return 1; } };"),
24967            CallableLinkage::External
24968        );
24969        assert_eq!(
24970            member_function_linkage(
24971                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
24972            ),
24973            CallableLinkage::Internal
24974        );
24975        assert_eq!(
24976            member_function_linkage("struct { int method() { return 1; } } instance;"),
24977            CallableLinkage::Internal
24978        );
24979        assert_eq!(
24980            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
24981            CallableLinkage::Internal
24982        );
24983    }
24984
24985    #[test]
24986    #[hardwon(
24987        "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24988    )]
24989    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
24990        let source = r#"
24991#ifndef PROTON_VALUE_HPP
24992#define PROTON_VALUE_HPP
24993namespace proton {
24994namespace internal {
24995class value_base {
24996  protected:
24997    internal::data& data();
24998    internal::data data_;
24999  friend class codec::encoder;
25000  friend class codec::decoder;
25001};
25002}
25003class value : public internal::value_base, private internal::comparable<value> {
25004  private:
25005    template<class T, class U=void> struct assignable :
25006        public std::enable_if<codec::is_encodable<T>::value, U> {};
25007    template<class U> struct assignable<value, U> {};
25008  public:
25009    PN_CPP_EXTERN value();
25010    PN_CPP_EXTERN value(const value&);
25011    PN_CPP_EXTERN value& operator=(const value&);
25012    PN_CPP_EXTERN value(value&&);
25013    PN_CPP_EXTERN value& operator=(value&&);
25014    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
25015    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
25016        codec::encoder e(*this);
25017        e << x;
25018        return *this;
25019    }
25020    PN_CPP_EXTERN type_id type() const;
25021    PN_CPP_EXTERN bool empty() const;
25022    PN_CPP_EXTERN void clear();
25023    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
25024    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
25025  friend PN_CPP_EXTERN void swap(value&, value&);
25026  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
25027  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
25028  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
25029    value(pn_data_t* d);
25030    void reset(pn_data_t* d = 0);
25031};
25032}
25033#endif
25034"#;
25035        let mut parser = tree_sitter::Parser::new();
25036        parser
25037            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25038            .unwrap();
25039        let tree = parser.parse(source, None).unwrap();
25040        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
25041        let parsed = parse_cpp_file(&file, source, &tree);
25042        // The macro-declaration grammar parses the class and its decorated
25043        // members cleanly, so the constructors mint with their true
25044        // class-owned identity (`proton.value.value`) rather than the
25045        // namespace-scope identity the fragment recovery produced before.
25046        let macro_constructors = parsed
25047            .signature_metadata
25048            .iter()
25049            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
25050            .flat_map(|(_, metadata)| metadata)
25051            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
25052            .collect::<Vec<_>>();
25053
25054        assert_eq!(
25055            macro_constructors.len(),
25056            3,
25057            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
25058            parsed.declarations()
25059        );
25060        assert!(
25061            macro_constructors.iter().all(|metadata| {
25062                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
25063            }),
25064            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
25065        );
25066    }
25067
25068    #[test]
25069    #[hardwon(
25070        "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
25071    )]
25072    fn recovered_export_class_typedef_uses_displaced_alias_name() {
25073        let source = r#"
25074namespace spi {
25075class Filter {
25076public:
25077    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
25078};
25079}
25080namespace filter {
25081class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
25082{
25083public:
25084    typedef spi::Filter BASE_CLASS;
25085    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
25086    BEGIN_LOG4CXX_CAST_MAP()
25087    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
25088    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
25089    END_LOG4CXX_CAST_MAP()
25090    FilterDecision decide() const;
25091};
25092}
25093"#;
25094        let mut parser = tree_sitter::Parser::new();
25095        parser
25096            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25097            .unwrap();
25098        let tree = parser.parse(source, None).unwrap();
25099        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
25100        let parsed = parse_cpp_file(&file, source, &tree);
25101        assert!(
25102            parsed.declarations().iter().any(|unit| {
25103                unit.is_class()
25104                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
25105                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
25106            }),
25107            "the displaced typedef alias must retain its declared name: {:#?}",
25108            parsed.declarations()
25109        );
25110        assert!(
25111            parsed
25112                .declarations()
25113                .iter()
25114                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
25115            "the qualified underlying type must not become a false nested alias: {:#?}",
25116            parsed.declarations()
25117        );
25118    }
25119
25120    #[test]
25121    #[hardwon(
25122        "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25123    )]
25124    fn exported_single_base_recovery_uses_displaced_class_name() {
25125        let source = r#"
25126class CORE_EXPORT QgsPoint : public AbstractGeometry
25127{
25128    Q_GADGET
25129
25130    Q_PROPERTY( double x READ x WRITE setX )
25131    Q_PROPERTY( double y READ y WRITE setY )
25132    Q_PROPERTY( double z READ z WRITE setZ )
25133    Q_PROPERTY( double m READ m WRITE setM )
25134
25135  public:
25136#ifndef SIP_RUN
25137    QgsPoint(
25138      double x = std::numeric_limits<double>::quiet_NaN(),
25139      double y = std::numeric_limits<double>::quiet_NaN(),
25140      double z = std::numeric_limits<double>::quiet_NaN(),
25141      double m = std::numeric_limits<double>::quiet_NaN(),
25142      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
25143    );
25144#else
25145    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 )];
25146    % MethodCode
25147    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25148    {
25149      int state;
25150      sipIsErr = 0;
25151      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25152      if ( !sipIsErr )
25153      {
25154        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25155      }
25156      sipReleaseType( p, sipType_QgsPointXY, state );
25157    }
25158    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25159    {
25160      int state;
25161      sipIsErr = 0;
25162
25163      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25164      if ( !sipIsErr )
25165      {
25166        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25167      }
25168      sipReleaseType( p, sipType_QPointF, state );
25169    }
25170    else if (
25171      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
25172      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
25173      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
25174      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
25175    {
25176      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
25177      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
25178      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
25179      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
25180      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
25181      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
25182    }
25183    else // Invalid ctor arguments
25184    {
25185      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
25186      sipIsErr = 1;
25187    }
25188    % End
25189#endif
25190
25191    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
25192    explicit QgsPoint( QPointF p ) SIP_SKIP;
25193    explicit QgsPoint(
25194      Qgis::WkbType wkbType,
25195      double x = std::numeric_limits<double>::quiet_NaN(),
25196      double y = std::numeric_limits<double>::quiet_NaN(),
25197      double z = std::numeric_limits<double>::quiet_NaN(),
25198      double m = std::numeric_limits<double>::quiet_NaN()
25199    ) SIP_SKIP;
25200    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25201    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
25202    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25203#ifndef SIP_RUN
25204  private:
25205    bool fuzzyHelper(
25206      double epsilon,
25207      const AbstractGeometry &other,
25208      bool is3DFlag,
25209      bool isMeasureFlag
25210    ) const
25211    {
25212      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
25213    }
25214#endif
25215};
25216class Ordinary : public Base { public: Ordinary(); };
25217class API_EXPORT Plain { public: Plain(); };
25218class API_EXPORT : public Base {};
25219class
25220PN_CPP_CLASS_EXTERN Sender : public Link {
25221    Sender();
25222    struct impl;
25223    struct impl& get_impl() const;
25224};
25225class thread_ctx_t {};
25226class ctx_t ZMQ_FINAL : public thread_ctx_t {
25227    bool start();
25228};
25229"#;
25230        let mut parser = tree_sitter::Parser::new();
25231        parser
25232            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25233            .unwrap();
25234        let tree = parser.parse(source, None).unwrap();
25235        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
25236        let parsed = parse_cpp_file(&file, source, &tree);
25237        let declarations = parsed.declarations();
25238
25239        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
25240            assert!(
25241                declarations
25242                    .iter()
25243                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
25244                "missing recovered class {expected}: {declarations:#?}"
25245            );
25246        }
25247        let qgs_point = declarations
25248            .iter()
25249            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
25250            .expect("recovered QgsPoint class");
25251        assert_eq!(
25252            parsed.raw_supertypes.get(qgs_point),
25253            Some(&vec!["AbstractGeometry".to_string()]),
25254            "single-base export recovery must retain its displaced base"
25255        );
25256        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
25257        assert!(
25258            parsed
25259                .navigation_ranges
25260                .get(qgs_point)
25261                .is_some_and(|ranges| {
25262                    !ranges.is_empty()
25263                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
25264                }),
25265            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
25266            parsed.navigation_ranges.get(qgs_point)
25267        );
25268        let sender = declarations
25269            .iter()
25270            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
25271            .expect("recovered Sender class");
25272        assert_eq!(
25273            parsed.raw_supertypes.get(sender),
25274            Some(&vec!["Link".to_string()]),
25275            "post-declarator export recovery must retain its displaced base"
25276        );
25277        let recovered_member = declarations
25278            .iter()
25279            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
25280            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
25281        assert_eq!(
25282            parsed
25283                .signature_metadata
25284                .get(recovered_member)
25285                .and_then(|metadata| metadata.first())
25286                .and_then(SignatureMetadata::callable_linkage),
25287            Some(CallableLinkage::External),
25288            "a named recovered class's members have external linkage"
25289        );
25290        let ctx = declarations
25291            .iter()
25292            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
25293            .expect("recovered ctx_t class");
25294        assert_eq!(
25295            parsed.raw_supertypes.get(ctx),
25296            Some(&vec!["thread_ctx_t".to_string()]),
25297            "postfix export-macro recovery must retain its displaced base"
25298        );
25299        assert!(
25300            declarations.iter().any(|unit| {
25301                unit.is_function()
25302                    && unit.fq_name() == "QgsPoint.QgsPoint"
25303                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
25304            }),
25305            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
25306        );
25307        assert!(
25308            declarations.iter().all(|unit| {
25309                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
25310            }),
25311            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
25312        );
25313    }
25314
25315    #[test]
25316    #[hardwon(
25317        "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25318    )]
25319    fn function_like_export_macro_classes_keep_names_and_base_edges() {
25320        // Every head shape after `class MACRO(2, 0)`: a plain body, a single
25321        // base, `final` without a base, `final` with one base, and `final` with
25322        // a comma-separated base list. Older grammars left each in its own
25323        // error shape; brokk-tree-sitter-cpp 0.24.7 reads them all as
25324        // `class_specifier`s, which publish an empty base list for a class
25325        // without bases.
25326        let source = r#"
25327namespace api {
25328class PROJECT_PUBLIC_API(2, 0) Prelude {
25329  public:
25330    Prelude();
25331};
25332class PROJECT_PUBLIC_API(2, 0) Base {
25333  public:
25334    Base(int value);
25335};
25336class PROJECT_PUBLIC_API(2, 0) Mixin {
25337  public:
25338    Mixin();
25339};
25340class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
25341  public:
25342    Adopted(int value);
25343};
25344class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25345  public:
25346    Derived(int value);
25347};
25348class PROJECT_PUBLIC_API(2, 0) Solo final {
25349  public:
25350    Solo();
25351};
25352class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25353  public:
25354    Blended(int value);
25355};
25356class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25357  public:
25358    Woven(int value);
25359};
25360} // namespace api
25361"#;
25362        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25363        let explicit = parse_cpp_declarations(
25364            &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
25365            "function-like-export.hpp",
25366        );
25367        assert_eq!(
25368            declaration_expansion_inventory(&parsed),
25369            declaration_expansion_inventory(&explicit)
25370        );
25371        let declarations = parsed.declarations();
25372        let class_named = |name: &str| {
25373            declarations
25374                .iter()
25375                .find(|unit| unit.is_class() && unit.fq_name() == name)
25376                .unwrap_or_else(|| {
25377                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25378                })
25379        };
25380        let base = class_named("api.Base");
25381        class_named("api.Prelude");
25382        class_named("api.Mixin");
25383
25384        assert_eq!(
25385            parsed.raw_supertypes.get(class_named("api.Adopted")),
25386            Some(&vec!["Base".to_string()])
25387        );
25388        assert_eq!(
25389            parsed.raw_supertypes.get(class_named("api.Derived")),
25390            Some(&vec!["Base".to_string()])
25391        );
25392        // The explicit expansion is the oracle for a class without bases: a
25393        // parsed class publishes an empty base list where recovery published
25394        // none.
25395        let solo = parsed.raw_supertypes.get(class_named("api.Solo"));
25396        assert_eq!(
25397            solo,
25398            explicit.raw_supertypes.get(class_named("api.Solo")),
25399            "a final class without a base list must match its explicit expansion"
25400        );
25401        assert!(
25402            solo.is_none_or(Vec::is_empty),
25403            "a final class without a base list must not invent a supertype"
25404        );
25405        assert_eq!(
25406            parsed.raw_supertypes.get(class_named("api.Blended")),
25407            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25408        );
25409        assert_eq!(
25410            parsed.raw_supertypes.get(class_named("api.Woven")),
25411            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25412        );
25413        assert!(
25414            declarations
25415                .iter()
25416                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25417            "the export macro must not become a declaration: {declarations:#?}"
25418        );
25419        assert!(
25420            declarations.iter().all(|unit| !matches!(
25421                unit.identifier(),
25422                "final" | "public" | "protected" | "private"
25423            )),
25424            "the head specifiers must not become declarations: {declarations:#?}"
25425        );
25426        assert!(
25427            parsed
25428                .navigation_ranges
25429                .get(base)
25430                .is_some_and(|ranges| !ranges.is_empty()),
25431            "the recovered base must retain a navigable declaration range"
25432        );
25433    }
25434
25435    #[test]
25436    #[hardwon(
25437        "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25438    )]
25439    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25440        // #2557: the class name is the last identifier before the head ends
25441        // (`final`, the base clause `:`, or the body), whatever its spelling.
25442        // A class named in capitals (`X509_CA`) is a class, and an object-like
25443        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
25444        // the control whose spelling never mattered.
25445        let source = r#"
25446namespace api {
25447class PROJECT_PUBLIC_API(2, 0) Base {
25448  public:
25449    Base();
25450};
25451class PROJECT_PUBLIC_API(2, 0) Mixin {
25452  public:
25453    Mixin();
25454};
25455class PROJECT_PUBLIC_API(2, 0) Name {
25456  public:
25457    Name();
25458};
25459class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25460  public:
25461    X509_CA();
25462};
25463class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25464  public:
25465    HSS_LMS_KEY();
25466};
25467class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25468  public:
25469    GOST_3410();
25470};
25471class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25472  public:
25473    PKCS11_RSA();
25474};
25475class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25476  public:
25477    Plain();
25478};
25479class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25480  public:
25481    Decorated();
25482};
25483class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25484  public:
25485    Layered();
25486};
25487} // namespace api
25488"#;
25489        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25490        let explicit = parse_cpp_declarations(
25491            &source
25492                .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25493                .replace("FIRST_MACRO SECOND_MACRO ", "")
25494                .replace("OTHER_MACRO ", ""),
25495            "positional-export.hpp",
25496        );
25497        assert_eq!(
25498            declaration_expansion_inventory(&parsed),
25499            declaration_expansion_inventory(&explicit)
25500        );
25501        let declarations = parsed.declarations();
25502        let class_named = |name: &str| {
25503            declarations
25504                .iter()
25505                .find(|unit| unit.is_class() && unit.fq_name() == name)
25506                .unwrap_or_else(|| {
25507                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25508                })
25509        };
25510        for (name, bases) in [
25511            ("api.Name", None),
25512            ("api.X509_CA", None),
25513            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25514            ("api.GOST_3410", Some(vec!["Base"])),
25515            ("api.PKCS11_RSA", None),
25516            ("api.Plain", None),
25517            ("api.Decorated", Some(vec!["Base"])),
25518            ("api.Layered", Some(vec!["Base", "Mixin"])),
25519        ] {
25520            let actual = parsed.raw_supertypes.get(class_named(name));
25521            match bases {
25522                Some(bases) => assert_eq!(
25523                    actual,
25524                    Some(&bases.into_iter().map(str::to_string).collect::<Vec<_>>()),
25525                    "{name}"
25526                ),
25527                // `MACRO(2, 0) OTHER_MACRO Plain {` without a base still reaches
25528                // recovery, which publishes no base list.
25529                None if name == "api.Plain" => assert_eq!(actual, None, "{name}"),
25530                // A head the grammar reads publishes the empty base list of
25531                // its explicit expansion.
25532                None => {
25533                    assert_eq!(
25534                        actual,
25535                        explicit.raw_supertypes.get(class_named(name)),
25536                        "{name} must match its explicit expansion"
25537                    );
25538                    assert!(actual.is_none_or(Vec::is_empty), "{name}");
25539                }
25540            }
25541        }
25542        assert!(
25543            declarations.iter().all(|unit| !matches!(
25544                unit.identifier(),
25545                "PROJECT_PUBLIC_API"
25546                    | "OTHER_MACRO"
25547                    | "FIRST_MACRO"
25548                    | "SECOND_MACRO"
25549                    | "final"
25550                    | "public"
25551            )),
25552            "macros and head specifiers must not become declarations: {declarations:#?}"
25553        );
25554    }
25555
25556    #[test]
25557    #[hardwon(
25558        "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
25559    )]
25560    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
25561        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
25562        // head, the body's `{` and its first member in one declaration-scope
25563        // ERROR, scatters the remaining members across the container's
25564        // siblings, and ends the container on the class's own `}`. The class
25565        // was lost and the macro minted a class of its own.
25566        let source = r#"
25567namespace api {
25568
25569/**
25570* Doc comment
25571*/
25572class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
25573   public:
25574      /**
25575      * Construct from a point.
25576      */
25577      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
25578
25579#if defined(PROJECT_HAS_LEGACY_POINT)
25580      /**
25581      * Construct from a legacy point.
25582      */
25583      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
25584#endif
25585
25586      std::string algo_name() const override;
25587
25588      AlgorithmIdentifier algorithm_identifier() const override;
25589};
25590
25591}
25592"#;
25593        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
25594        let declarations = parsed.declarations();
25595        let class = declarations
25596            .iter()
25597            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
25598            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25599        assert_eq!(
25600            parsed.raw_supertypes.get(class),
25601            Some(&vec!["BaseKey".to_string()]),
25602            "the virtual base is the class's base: {declarations:#?}"
25603        );
25604        for member in [
25605            "api.VirtualBased.algo_name",
25606            "api.VirtualBased.algorithm_identifier",
25607        ] {
25608            assert!(
25609                declarations
25610                    .iter()
25611                    .any(|unit| unit.is_function() && unit.fq_name() == member),
25612                "{member} must be owned by the recovered class: {declarations:#?}"
25613            );
25614        }
25615        assert!(
25616            declarations
25617                .iter()
25618                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25619            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
25620        );
25621    }
25622
25623    #[test]
25624    #[hardwon(
25625        "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
25626    )]
25627    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
25628        // #2924: object-like macro lines before the head demote the `class`
25629        // keyword to a bare identifier and make the macro invocation an
25630        // `init_declarator`, so the head arrives as a `declaration` rather than
25631        // as an ERROR carrying a `class_specifier`. The name is still the last
25632        // identifier before `final`, and `virtual` is a base specifier rather
25633        // than a base.
25634        let source = r#"
25635namespace api {
25636
25637DIAGNOSTIC_PUSH
25638DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
25639
25640class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
25641                                               public virtual api::Inner::Key {
25642   public:
25643      std::string algo_name() const override;
25644};
25645
25646DIAGNOSTIC_POP
25647
25648}
25649"#;
25650        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
25651        let declarations = parsed.declarations();
25652        let class = declarations
25653            .iter()
25654            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
25655            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25656        assert_eq!(
25657            parsed.raw_supertypes.get(class),
25658            Some(&vec![
25659                "api::Outer::Key".to_string(),
25660                "api::Inner::Key".to_string()
25661            ]),
25662            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
25663        );
25664        assert!(
25665            declarations
25666                .iter()
25667                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
25668            "the member is owned by the recovered class: {declarations:#?}"
25669        );
25670        assert!(
25671            declarations
25672                .iter()
25673                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25674            "the macro invocation must not mint a declaration: {declarations:#?}"
25675        );
25676    }
25677
25678    fn function_like_export_class_fixture(head: &str, name: &str) -> String {
25679        format!(
25680            r#"
25681namespace api {{
25682class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
25683   public:
25684      /** Return a descriptive string. */
25685      const char* what() const noexcept override {{ return m_msg.c_str(); }}
25686
25687      /** Return the type of error. */
25688      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
25689
25690      /** Return an associated error code. */
25691      virtual int error_code() const noexcept {{ return 0; }}
25692
25693      /** Avoid throwing the base directly. */
25694      explicit Exception(std::string_view msg);
25695
25696      /** Avoid throwing the base directly. */
25697      Exception(const char* prefix, std::string_view msg);
25698
25699      /** Avoid throwing the base directly. */
25700      Exception(std::string_view msg, const std::exception& e);
25701
25702   private:
25703      std::string m_msg;
25704}};
25705
25706class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
25707   public:
25708      explicit {name}(std::string_view msg);
25709
25710      explicit {name}(std::string_view msg, std::string_view where);
25711
25712      {name}(std::string_view msg, const std::exception& e);
25713
25714      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
25715}};
25716}} // namespace api
25717"#
25718        )
25719    }
25720
25721    #[test]
25722    #[hardwon(
25723        "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
25724    )]
25725    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
25726        // The class after a preceding export class follows the same rule
25727        // (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration. Older
25728        // grammars embedded this head in the preceding class's error; since
25729        // brokk-tree-sitter-cpp 0.24.7 the grammar reads both heads, and the
25730        // class must still be named by position.
25731        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
25732            let source = function_like_export_class_fixture(head, name);
25733            let tree = crate::grammar_test_support::tree(&source);
25734            crate::grammar_test_support::assert_function_like_export_class_head(
25735                &tree, &source, name,
25736            );
25737
25738            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
25739            let explicit = parse_cpp_declarations(
25740                &source
25741                    .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25742                    .replace("OTHER_MACRO ", ""),
25743                "embedded-positional-export.hpp",
25744            );
25745            assert_eq!(
25746                declaration_expansion_inventory(&parsed),
25747                declaration_expansion_inventory(&explicit),
25748                "{head}"
25749            );
25750            let declarations = parsed.declarations();
25751            let class = declarations
25752                .iter()
25753                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
25754                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
25755            assert_eq!(
25756                parsed.raw_supertypes.get(class),
25757                Some(&vec!["Exception".to_string()]),
25758                "{head}"
25759            );
25760            assert!(
25761                declarations
25762                    .iter()
25763                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
25764                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
25765            );
25766        }
25767    }
25768
25769    #[test]
25770    #[hardwon(
25771        "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
25772    )]
25773    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
25774        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
25775        // `class MACRO(3, 6) Name final :` in the error, the first base as the
25776        // `type` of the following declaration, and closes that declaration with
25777        // a zero-width `MISSING identifier`. Reading the head by position must
25778        // not take that missing node for the class name: an empty name panics
25779        // at `FqName` construction, which took the Botan corpus replay down
25780        // (#2557). The head's tokens straddle the two nodes, so the positional
25781        // read spans both and recovers the class rather than leaving the
25782        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
25783        let source = r#"
25784namespace api {
25785class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
25786                                                    public virtual Botan::EC_PublicKey {
25787   public:
25788      std::string algo_name() const override { return "ECDSA"; }
25789};
25790} // namespace api
25791"#;
25792        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
25793        let declarations = parsed.declarations();
25794        assert!(
25795            declarations
25796                .iter()
25797                .all(|unit| !unit.identifier().is_empty()),
25798            "no declaration may carry an empty name: {declarations:#?}"
25799        );
25800        assert!(
25801            declarations
25802                .iter()
25803                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
25804            "macros and head specifiers must not become declarations: {declarations:#?}"
25805        );
25806        let class = declarations
25807            .iter()
25808            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
25809            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25810        assert_eq!(
25811            parsed.raw_supertypes.get(class),
25812            Some(&vec![
25813                "Botan::TPM2::PublicKey".to_string(),
25814                "Botan::EC_PublicKey".to_string()
25815            ]),
25816            "both qualified virtual bases are bases: {declarations:#?}"
25817        );
25818        let source_facts = parsed.source_facts.as_ref().unwrap();
25819        let declaration = parsed
25820            .source_declaration_units
25821            .iter()
25822            .find(|(_, unit)| unit == class)
25823            .map(|(declaration, _)| *declaration)
25824            .expect("recovered class source bridge");
25825        let bases = source_facts
25826            .cpp
25827            .as_ref()
25828            .unwrap()
25829            .declarations
25830            .iter()
25831            .find(|fact| fact.declaration == declaration)
25832            .expect("recovered class source fact")
25833            .bases
25834            .clone();
25835        assert_eq!(
25836            bases
25837                .iter()
25838                .map(|base| base.components.clone())
25839                .collect::<Vec<_>>(),
25840            vec![
25841                vec!["Botan", "TPM2", "PublicKey"],
25842                vec!["Botan", "EC_PublicKey"],
25843            ],
25844            "generalized recovery must publish structured base components"
25845        );
25846        assert!(
25847            bases.iter().all(|base| base.is_virtual),
25848            "generalized recovery must preserve virtual base qualifiers"
25849        );
25850        assert!(
25851            declarations
25852                .iter()
25853                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25854            "the head must not mint a macro-named class: {declarations:#?}"
25855        );
25856        let mixed_source = source.replace(
25857            "public virtual Botan::EC_PublicKey",
25858            "public Botan::EC_PublicKey",
25859        );
25860        let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
25861        let qualifiers = mixed
25862            .source_facts
25863            .as_ref()
25864            .unwrap()
25865            .cpp
25866            .as_ref()
25867            .unwrap()
25868            .declarations
25869            .iter()
25870            .flat_map(|declaration| &declaration.bases)
25871            .map(|base| base.is_virtual)
25872            .collect::<Vec<_>>();
25873        assert_eq!(
25874            qualifiers,
25875            [true, false],
25876            "base qualifiers must not cross the comma"
25877        );
25878    }
25879
25880    #[test]
25881    #[hardwon(
25882        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25883    )]
25884    fn function_like_export_class_survives_a_preceding_malformed_body() {
25885        // Older grammars left the first class's body malformed and embedded the
25886        // second class in its error. Since brokk-tree-sitter-cpp 0.24.7 both
25887        // heads parse, so both classes are ordinary source declarations.
25888        let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
25889        let tree = crate::grammar_test_support::tree(&source);
25890        for name in ["Exception", "Invalid_Argument"] {
25891            crate::grammar_test_support::assert_function_like_export_class_head(
25892                &tree, &source, name,
25893            );
25894        }
25895
25896        let parsed = parse_cpp_file(
25897            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25898            &source,
25899            &tree,
25900        );
25901        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
25902        let explicit = parse_cpp_file(
25903            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25904            &expanded,
25905            &crate::grammar_test_support::tree(&expanded),
25906        );
25907        assert_eq!(
25908            declaration_expansion_inventory(&parsed),
25909            declaration_expansion_inventory(&explicit)
25910        );
25911        assert!(
25912            !explicit
25913                .materialization_records
25914                .iter()
25915                .any(|record| matches!(record, MaterializationRecord::RecoveredDeclaration { .. })),
25916            "the explicit expansion needs no recovery: {:#?}",
25917            explicit.materialization_records
25918        );
25919        let declarations = parsed.declarations();
25920        let exception = declarations
25921            .iter()
25922            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
25923            .expect("qualified-base export class");
25924        let invalid = declarations
25925            .iter()
25926            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
25927            .expect("class embedded in the preceding malformed body");
25928
25929        assert_eq!(
25930            parsed.raw_supertypes.get(exception),
25931            Some(&vec!["std::exception".to_string()])
25932        );
25933        assert_eq!(
25934            parsed.raw_supertypes.get(invalid),
25935            Some(&vec!["Exception".to_string()])
25936        );
25937        assert!(
25938            !parsed.materialization_records.iter().any(|record| matches!(
25939                record,
25940                MaterializationRecord::RecoveredDeclaration { unit, .. }
25941                    if unit == invalid
25942            )),
25943            "a parsed class must not claim recovery provenance: {:#?}",
25944            parsed.materialization_records
25945        );
25946    }
25947
25948    #[test]
25949    #[hardwon(
25950        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25951    )]
25952    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
25953        let source = r#"
25954public:
25955   explicit Lookup_Error(std::string_view err) : Exception(err) {}
25956
25957   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
25958"#;
25959        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
25960            .expect("reparse merged constructor body");
25961        let (range, body) =
25962            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
25963                .unwrap_or_else(|| {
25964                    panic!(
25965                        "the merged constructor must retain its structured declarator/body: {}",
25966                        tree.root_node().to_sexp()
25967                    )
25968                });
25969        assert_eq!(
25970            source.get(range).expect("constructor range"),
25971            "Lookup_Error(std::string_view err) : Exception(err) {}"
25972        );
25973        assert_eq!(node_text(body, source), "{}");
25974    }
25975
25976    #[test]
25977    #[hardwon(
25978        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25979    )]
25980    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
25981        let positive_source =
25982            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
25983        let mut parser = tree_sitter::Parser::new();
25984        parser
25985            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25986            .unwrap();
25987        let positive_tree = parser.parse(positive_source, None).unwrap();
25988        assert!(cpp_reparsed_members_are_indexable(
25989            positive_tree.root_node(),
25990            positive_source
25991        ));
25992
25993        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
25994        let negative_tree = parser.parse(negative_source, None).unwrap();
25995        assert!(!cpp_reparsed_members_are_indexable(
25996            negative_tree.root_node(),
25997            negative_source
25998        ));
25999    }
26000
26001    #[test]
26002    #[hardwon(
26003        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26004    )]
26005    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
26006        let copy_control_source = r#"
26007public:
26008    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
26009    explicit Token(const Token* tok);
26010    ~Token();
26011    Token* astOperand1() { return nullptr; }
26012"#;
26013        let constraint_source = r#"
26014private:
26015    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
26016    static T *tokAtImpl(T *tok, int index) {
26017        return tok;
26018    }
26019
26020    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
26021    static T *linkAtImpl(T *tok, int index) {
26022        return tok;
26023    }
26024
26025public:
26026    int late() const { return 1; }
26027"#;
26028        let mut parser = tree_sitter::Parser::new();
26029        parser
26030            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26031            .unwrap();
26032        let copy_control_tree = parser
26033            .parse(copy_control_source, None)
26034            .expect("parse copy-control fixture");
26035        assert!(
26036            copy_control_tree.root_node().has_error(),
26037            "fixture must exercise adjacent copy-control recovery"
26038        );
26039        assert!(
26040            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
26041            "a complete late getter must remain recoverable after adjacent copy-control declarations"
26042        );
26043        let mut cursor = copy_control_tree.root_node().walk();
26044        assert!(
26045            copy_control_tree
26046                .root_node()
26047                .named_children(&mut cursor)
26048                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
26049            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
26050            copy_control_tree.root_node().to_sexp()
26051        );
26052        let constraint_tree = parser
26053            .parse(constraint_source, None)
26054            .expect("parse constraint-macro fixture");
26055        assert!(constraint_tree.root_node().has_error());
26056        assert!(
26057            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
26058            "complete constraint-macro members must not hide a later ordinary member"
26059        );
26060        let mut cursor = constraint_tree.root_node().walk();
26061        assert!(
26062            constraint_tree
26063                .root_node()
26064                .named_children(&mut cursor)
26065                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
26066                    child,
26067                    constraint_source
26068                )),
26069            "fixture must retain the split constraint-macro prefix/function geometry"
26070        );
26071    }
26072
26073    #[test]
26074    #[hardwon(
26075        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26076    )]
26077    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
26078        let source = r#"
26079struct Analyzer {
26080    struct Action {
26081        Action() = default;
26082        Action(const Action&) = default;
26083        Action& operator=(const Action& rhs) & = default;
26084
26085        template<class T,
26086                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
26087                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
26088        // NOLINTNEXTLINE(google-explicit-constructor)
26089        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
26090        {}
26091
26092        enum : std::uint16_t { None = 0, Read = (1 << 0) };
26093        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
26094
26095    private:
26096        unsigned int mFlag{};
26097    };
26098
26099    enum class Direction : unsigned char { Forward, Reverse };
26100    virtual Action analyze(Direction d) const = 0;
26101};
26102"#;
26103        let mut parser = tree_sitter::Parser::new();
26104        parser
26105            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26106            .unwrap();
26107        let tree = parser.parse(source, None).unwrap();
26108        assert!(tree.root_node().has_error());
26109        let root = tree.root_node();
26110        let outer = root
26111            .named_children(&mut root.walk())
26112            .find(|child| child.kind() == "ERROR")
26113            .expect("fragmented Analyzer prefix");
26114        let outer_recovered =
26115            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
26116        assert_eq!(outer_recovered.name, "Analyzer");
26117        let outer_tree = cpp_reparse_fragmented_class_body(
26118            source,
26119            outer_recovered.body.reparse_start,
26120            outer_recovered.body.reparse_end,
26121        )
26122        .expect("reparse Analyzer body");
26123        let outer_root = outer_tree.root_node();
26124        let action_prefix = outer_root
26125            .named_children(&mut outer_root.walk())
26126            .find(|child| child.kind() == "ERROR")
26127            .expect("fragmented Action prefix");
26128        let action_recovered = fragmented_class_body(action_prefix, source)
26129            .expect("structured Action fragment boundary");
26130        assert_eq!(action_recovered.name, "Action");
26131        let action_tree = cpp_reparse_fragmented_class_body(
26132            source,
26133            action_recovered.body.reparse_start,
26134            action_recovered.body.reparse_end,
26135        )
26136        .expect("reparse Action body");
26137        let action_root = action_tree.root_node();
26138        let macro_prefix = action_root
26139            .named_children(&mut action_root.walk())
26140            .find(|child| child.kind() == "ERROR")
26141            .expect("constraint macro prefix");
26142        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
26143            .expect("structured template macro prefix");
26144        let macro_companion =
26145            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
26146        assert!(
26147            cpp_reparsed_template_macro_constructor_companion_is_indexable(
26148                macro_companion,
26149                macro_parameter,
26150                source,
26151            ),
26152            "split constrained constructor must be admitted: {}",
26153            macro_companion.to_sexp()
26154        );
26155        assert!(
26156            cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
26157            "complete Action body must pass the recovery gate: {}",
26158            action_tree.root_node().to_sexp()
26159        );
26160        assert!(
26161            cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
26162            "complete Analyzer body must pass the recovery gate: {}",
26163            outer_tree.root_node().to_sexp()
26164        );
26165        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
26166        let parsed = parse_cpp_file(&file, source, &tree);
26167        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
26168            assert!(
26169                parsed
26170                    .declarations()
26171                    .iter()
26172                    .any(|unit| unit.fq_name() == expected),
26173                "missing recovered declaration {expected}: {:#?}",
26174                parsed.declarations()
26175            );
26176        }
26177        assert!(
26178            parsed
26179                .declarations()
26180                .iter()
26181                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
26182            "nested members must not remain flattened: {:#?}",
26183            parsed.declarations()
26184        );
26185    }
26186
26187    #[test]
26188    #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
26189    fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
26190        let source = "value() = default;\nint value();\n";
26191        let tree = crate::diagnostics::parse_cpp_tree(source);
26192        let root = tree.root_node();
26193        assert!(
26194            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
26195            "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
26196            root.to_sexp()
26197        );
26198        assert!(
26199            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
26200            "a return-typed same-name declaration is not a constructor witness: {}",
26201            root.to_sexp()
26202        );
26203
26204        let damaged_source = r#"
26205value(int x) {
26206  broken(
26207}
26208value() = default;
26209int value();
26210"#;
26211        let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
26212        let damaged_root = damaged_tree.root_node();
26213        // The macro-declaration grammar absorbs the whole damaged witness
26214        // into one errorful `function_definition`, so the default-assignment
26215        // expression lives inside the bogus body rather than as a top-level
26216        // sibling. The gate's answer must not change: the assignment is not
26217        // the class-named call, and the errorful same-name function is not a
26218        // separate constructor witness.
26219        let mut damaged_stack = vec![damaged_root];
26220        let mut damaged_assignment = None;
26221        while let Some(node) = damaged_stack.pop() {
26222            if node.kind() == "expression_statement" {
26223                damaged_assignment = Some(node);
26224                break;
26225            }
26226            let mut cursor = node.walk();
26227            damaged_stack.extend(node.named_children(&mut cursor));
26228        }
26229        let damaged_assignment =
26230            damaged_assignment.expect("damaged witness probe retains the assignment expression");
26231        let mut damaged_cursor = damaged_root.walk();
26232        let damaged_members = damaged_root
26233            .named_children(&mut damaged_cursor)
26234            .collect::<Vec<_>>();
26235        assert!(
26236            !cpp_reparsed_defaulted_constructor_is_indexable(
26237                damaged_assignment,
26238                &damaged_members,
26239                Some("value"),
26240                damaged_source,
26241            ),
26242            "an errorful same-name function must not witness a recovered defaulted constructor: {}",
26243            damaged_root.to_sexp()
26244        );
26245        assert!(
26246            !cpp_reparsed_members_are_indexable_for_class(
26247                damaged_root,
26248                damaged_source,
26249                Some("value")
26250            ),
26251            "the damaged witness must not pass the member gate either: {}",
26252            damaged_root.to_sexp()
26253        );
26254    }
26255
26256    #[test]
26257    #[hardwon(
26258        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26259    )]
26260    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
26261        let source = r#"
26262raw_hash_set& operator=(raw_hash_set&& that) {
26263  return move_assign(
26264      std::move(that),
26265      typename AllocTraits::propagate_on_container_move_assignment());
26266}
26267
26268iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
26269  return {};
26270}
26271
26272void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
26273
26274iterator insert(const_iterator hint, value_type&& value)
26275    ABSL_ATTRIBUTE_LIFETIME_BOUND {
26276  return {};
26277}
26278
26279friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
26280  return left.size() == right.size();
26281}
26282
26283static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
26284  return static_cast<slot_type*>(buffer);
26285}
26286
26287protected:
26288// Included-range recovery can attach this comment to the template prefix.
26289template <class K>
26290void AssertOnFind([[maybe_unused]] const K& key) {
26291  Check(key);
26292}
26293"#;
26294        let mut parser = tree_sitter::Parser::new();
26295        parser
26296            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26297            .unwrap();
26298        let tree = parser.parse(source, None).unwrap();
26299        assert!(
26300            tree.root_node().has_error(),
26301            "the fixture must exercise tree-sitter's errorful member shapes"
26302        );
26303        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
26304
26305        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
26306        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
26307        assert!(!cpp_reparsed_members_are_indexable(
26308            incomplete_tree.root_node(),
26309            incomplete_source
26310        ));
26311
26312        let outside_error_source = "int foo() stray_attribute {}\n";
26313        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
26314        assert!(outside_error_tree.root_node().has_error());
26315        assert!(!cpp_reparsed_members_are_indexable(
26316            outside_error_tree.root_node(),
26317            outside_error_source
26318        ));
26319
26320        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
26321        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26322        assert!(!cpp_reparsed_members_are_indexable(
26323            variable_initializer_tree.root_node(),
26324            variable_initializer_source
26325        ));
26326    }
26327
26328    #[test]
26329    #[hardwon(
26330        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26331    )]
26332    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
26333        let positive_source = r#"
26334std::pair<iterator, bool> insert(init_type&& value)
26335    ABSL_ATTRIBUTE_LIFETIME_BOUND
26336#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26337  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26338#endif
26339{
26340  return emplace(std::move(value));
26341}
26342"#;
26343        let mut parser = tree_sitter::Parser::new();
26344        parser
26345            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26346            .unwrap();
26347        let positive_tree = parser.parse(positive_source, None).unwrap();
26348        assert!(
26349            positive_tree.root_node().has_error(),
26350            "the fixture must exercise the split attribute/requires shape"
26351        );
26352        assert!(cpp_reparsed_members_are_indexable(
26353            positive_tree.root_node(),
26354            positive_source
26355        ));
26356
26357        let template_return_source = r#"
26358pair<int> insert(init_type&& value)
26359    ABSL_ATTRIBUTE_LIFETIME_BOUND
26360#if LANGUAGE_LEVEL >= 202002L
26361  requires(!Predicate<init_type>::value)
26362#endif
26363// Attributes and the function body may be separated by comments.
26364{
26365  return {};
26366}
26367"#;
26368        let template_return_tree = parser.parse(template_return_source, None).unwrap();
26369        assert!(
26370            cpp_reparsed_members_are_indexable(
26371                template_return_tree.root_node(),
26372                template_return_source
26373            ),
26374            "template-return attribute/requires tree: {}",
26375            template_return_tree.root_node().to_sexp()
26376        );
26377
26378        let no_body_source = r#"
26379std::pair<iterator, bool> insert(init_type&& value)
26380    ABSL_ATTRIBUTE_LIFETIME_BOUND
26381#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26382  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26383#endif
26384+ 0;
26385"#;
26386        let no_body_tree = parser.parse(no_body_source, None).unwrap();
26387        assert!(!cpp_reparsed_members_are_indexable(
26388            no_body_tree.root_node(),
26389            no_body_source
26390        ));
26391
26392        let extra_payload_source = r#"
26393pair<int> insert(init_type&& value)
26394    ABSL_ATTRIBUTE_LIFETIME_BOUND
26395#if LANGUAGE_LEVEL >= 202002L
26396  int unrelated;
26397  requires(Predicate<init_type>::value)
26398#endif
26399{
26400  return {};
26401}
26402"#;
26403        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26404        assert!(!cpp_reparsed_members_are_indexable(
26405            extra_payload_tree.root_node(),
26406            extra_payload_source
26407        ));
26408
26409        let variable_initializer_source = r#"
26410int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26411#if LANGUAGE_LEVEL >= 202002L
26412  requires(true)
26413#endif
26414{
26415  bad;
26416}
26417"#;
26418        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26419        assert!(!cpp_reparsed_members_are_indexable(
26420            variable_initializer_tree.root_node(),
26421            variable_initializer_source
26422        ));
26423    }
26424
26425    #[test]
26426    #[hardwon(
26427        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26428    )]
26429    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26430        let source = r#"namespace absl {
26431ABSL_NAMESPACE_BEGIN namespace container_internal {
26432
26433class raw_hash_set : public Base {
26434 public:
26435  using value_type = int;
26436
26437  template <class U,
26438            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26439  void insert(U value) { (void)value; }
26440
26441  struct InsertSlot {
26442    raw_hash_set& s;
26443  };
26444};
26445
26446}
26447ABSL_NAMESPACE_END
26448}"#;
26449        let mut parser = tree_sitter::Parser::new();
26450        parser
26451            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26452            .unwrap();
26453        let tree = parser.parse(source, None).unwrap();
26454        let root = tree.root_node();
26455        let outer_namespace = root
26456            .named_children(&mut root.walk())
26457            .find(|child| child.kind() == "namespace_definition")
26458            .expect("outer absl namespace");
26459        let declaration_list = outer_namespace
26460            .child_by_field_name("body")
26461            .expect("outer namespace body");
26462        let sentinel_function = declaration_list
26463            .named_children(&mut declaration_list.walk())
26464            .find(|child| child.kind() == "function_definition")
26465            .expect("malformed namespace sentinel function");
26466        let ancestry = ParentIndex::new(root);
26467        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26468            .expect("structured nested namespace sentinel");
26469        let fragmented =
26470            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26471                .expect("fragmented raw_hash_set class");
26472        assert_eq!(fragmented.class_node.kind(), "ERROR");
26473        assert_eq!(fragmented.name, "raw_hash_set");
26474        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26475
26476        let outer_scope =
26477            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26478        let mut outer_siblings = Vec::new();
26479        push_cpp_sentinel_sibling_classes(
26480            &mut outer_siblings,
26481            declaration_list,
26482            sentinel.function,
26483            &outer_scope,
26484            source,
26485            &ancestry,
26486        );
26487        let [outer_shadow] = outer_siblings.as_slice() else {
26488            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26489        };
26490        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26491        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26492
26493        let field = "    raw_hash_set& s;";
26494        let start = source.find(field).expect("InsertSlot field") + 4;
26495        let node = root
26496            .descendant_for_byte_range(start, start + "raw_hash_set".len())
26497            .expect("raw_hash_set type node");
26498        let recovered = cpp_sentinel_recovered_classes(root, source);
26499        let [deep_class] = recovered.as_slice() else {
26500            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26501        };
26502        assert_eq!(
26503            deep_class.namespace_scope_components,
26504            vec!["absl", "container_internal"]
26505        );
26506        assert_eq!(
26507            deep_class.scope_components,
26508            vec!["absl", "container_internal", "raw_hash_set"]
26509        );
26510        assert!(
26511            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26512                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26513        );
26514
26515        assert_eq!(
26516            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26517            Some(vec![
26518                "absl".to_string(),
26519                "container_internal".to_string(),
26520                "raw_hash_set".to_string(),
26521                "InsertSlot".to_string(),
26522            ])
26523        );
26524
26525        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26526        let parsed = parse_cpp_file(&file, source, &tree);
26527        let raw_hash_set = parsed
26528            .declarations()
26529            .iter()
26530            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26531            .expect("recovered raw_hash_set class");
26532        assert_eq!(
26533            raw_hash_set.fq_name(),
26534            "absl::container_internal.raw_hash_set",
26535            "the recovered declaration must publish under the deeper sentinel namespace"
26536        );
26537        assert_eq!(
26538            parsed.raw_supertypes.get(raw_hash_set),
26539            Some(&vec!["Base".to_string()]),
26540            "the structured base clause on the fragmented ERROR prefix must survive publication"
26541        );
26542        assert!(
26543            parsed.materialization_records.iter().any(|record| matches!(
26544                record,
26545                MaterializationRecord::RecoveredDeclaration { recovery, unit }
26546                    if unit == raw_hash_set && *recovery == deep_class.class_range
26547            )),
26548            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26549            parsed.materialization_records
26550        );
26551    }
26552
26553    /// Issue #2358: recording an aggregate definition must not walk the whole
26554    /// file.
26555    ///
26556    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
26557    /// class-like shape that has a body, so the removal step runs once per
26558    /// aggregate. It used to `retain` over `top_level_declarations` and over
26559    /// *every* child list in the file on each of those calls, comparing whole
26560    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
26561    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
26562    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
26563    /// tens of thousands of times over and the C forward differential never
26564    /// finished.
26565    ///
26566    /// A definition the file has not already declared removes nothing, so the
26567    /// honest cost is zero regardless of how many other aggregates surround it.
26568    /// Two sizes an order of magnitude apart pin that the count is not merely
26569    /// small but independent of the file.
26570    ///
26571    /// The declaration walk answers every ancestor question from a
26572    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
26573    /// the root for each one (#2361). Substituting the index is only safe
26574    /// because it answers the identical question, so pin that on the shapes
26575    /// this file's recovery paths care about: anonymous and named aggregates,
26576    /// nested namespaces, templates, macro-displaced declarations and the
26577    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
26578    /// too -- `Node::parent` walks the visible tree, not the named one.
26579    #[test]
26580    #[hardwon(
26581        "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
26582    )]
26583    fn the_parent_index_answers_what_tree_sitter_answers() {
26584        const SHAPES: [&str; 5] = [
26585            "namespace outer { namespace inner { struct Tag { int field; }; } }",
26586            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
26587            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
26588            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
26589            "class API Broken : public First, public Second {\n  void member();\n",
26590        ];
26591        for source in SHAPES {
26592            let mut parser = tree_sitter::Parser::new();
26593            parser
26594                .set_language(&tree_sitter_cpp::LANGUAGE.into())
26595                .unwrap();
26596            let tree = parser.parse(source, None).unwrap();
26597            let root = tree.root_node();
26598            let ancestry = ParentIndex::new(root);
26599            let mut nodes = 0usize;
26600            let mut stack = vec![root];
26601            while let Some(node) = stack.pop() {
26602                nodes += 1;
26603                assert_eq!(
26604                    node.parent().map(|parent| parent.id()),
26605                    ancestry.parent(node).map(|parent| parent.id()),
26606                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
26607                );
26608                let mut cursor = node.walk();
26609                stack.extend(node.children(&mut cursor));
26610            }
26611            assert!(nodes > 1, "{source:?} produced no tree to compare");
26612        }
26613    }
26614
26615    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
26616    /// index for every ancestor edge. Asking tree-sitter directly makes each
26617    /// edge re-descend from the root, turning declaration extraction on a
26618    /// deeply nested generated header from quadratic output work into a cubic
26619    /// tree walk. Exact query counts pin the route without a machine-dependent
26620    /// wall-clock ceiling.
26621    #[test]
26622    #[hardwon(
26623        "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
26624    )]
26625    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
26626        const DEPTH: usize = 64;
26627        let mut source = String::new();
26628        for level in 0..DEPTH {
26629            writeln!(source, "namespace n{level} {{").unwrap();
26630        }
26631        source.push_str("int deepest(int value);\n");
26632        for _ in 0..DEPTH {
26633            source.push_str("}\n");
26634        }
26635
26636        let mut parser = tree_sitter::Parser::new();
26637        parser
26638            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26639            .unwrap();
26640        let tree = parser.parse(&source, None).unwrap();
26641        let root = tree.root_node();
26642        let ancestry = ParentIndex::new(root);
26643        let mut function_declarator = None;
26644        walk_named_tree_preorder(root, true, |node| {
26645            if node.kind() == "function_declarator" {
26646                function_declarator = Some(node);
26647                WalkControl::Break
26648            } else {
26649                WalkControl::Continue
26650            }
26651        });
26652        let function_declarator = function_declarator.expect("deepest function declarator");
26653        let ancestor_count =
26654            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
26655
26656        ancestry.reset_parent_query_count_for_test();
26657        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
26658        assert_eq!(DEPTH, lexical_scope.len());
26659        assert_eq!(
26660            ancestor_count + 1,
26661            ancestry.parent_query_count_for_test(),
26662            "lexical-scope ancestry bypassed the parent index"
26663        );
26664
26665        ancestry.reset_parent_query_count_for_test();
26666        assert_eq!(
26667            Some(DispatchExtensibility::Closed),
26668            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
26669        );
26670        assert_eq!(
26671            ancestor_count,
26672            ancestry.parent_query_count_for_test(),
26673            "dispatch ancestry bypassed the parent index"
26674        );
26675
26676        ancestry.reset_parent_query_count_for_test();
26677        assert_eq!(
26678            CallableLinkage::External,
26679            cpp_callable_linkage(function_declarator, &source, &ancestry)
26680        );
26681        assert_eq!(
26682            ancestor_count + 1,
26683            ancestry.parent_query_count_for_test(),
26684            "linkage ancestry bypassed the parent index"
26685        );
26686
26687        ancestry.reset_parent_query_count_for_test();
26688        assert!(!cpp_callable_is_structural_constructor(
26689            function_declarator,
26690            &source,
26691            &ancestry
26692        ));
26693        assert_eq!(
26694            ancestor_count + 1,
26695            ancestry.parent_query_count_for_test(),
26696            "constructor ancestry bypassed the parent index"
26697        );
26698    }
26699
26700    /// #3507: the one row-level dispatch fact C++ can state is stated only
26701    /// where the language states it -- in the class body.
26702    ///
26703    /// A call's exactness is not readable from a definition row: an
26704    /// out-of-line definition repeats no `virtual`, no `final`, and no base
26705    /// clause, and C++ inherits virtual-ness from a base that declares the
26706    /// member. Each shape below therefore has to answer for itself, and only
26707    /// a class body with no base clause at all can close a member that writes
26708    /// nothing.
26709    #[test]
26710    #[hardwon(
26711        "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
26712    )]
26713    fn callable_dispatch_extensibility_comes_from_the_class_body() {
26714        const SOURCE: &str = "\
26715class Plain { public: long send(long order); };
26716class Derived : public Base { public: long send(long order); };
26717class Sealed : public Base { public: long send(long order) final; };
26718class Hiding : public Base { public: long send(long order) { return order; } };
26719class Virtual { public: virtual long send(long order); };
26720namespace shop { long free_call(long order); }
26721long Plain::send(long order) { return order; }
26722";
26723        let mut parser = tree_sitter::Parser::new();
26724        parser
26725            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26726            .unwrap();
26727        let tree = parser.parse(SOURCE, None).unwrap();
26728        let root = tree.root_node();
26729        let ancestry = ParentIndex::new(root);
26730        let mut answers = Vec::new();
26731        walk_named_tree_preorder(root, true, |node| {
26732            if node.kind() != "function_declarator" {
26733                return WalkControl::Continue;
26734            }
26735            let name = cpp_function_declarator_name_node(node).expect("declarator name");
26736            answers.push((
26737                node_text(name, SOURCE).to_string(),
26738                cpp_callable_dispatch_extensibility(node, &ancestry),
26739            ));
26740            WalkControl::Continue
26741        });
26742        assert_eq!(
26743            answers,
26744            vec![
26745                ("send".to_string(), Some(DispatchExtensibility::Closed)),
26746                ("send".to_string(), Some(DispatchExtensibility::Open)),
26747                ("send".to_string(), Some(DispatchExtensibility::Closed)),
26748                ("send".to_string(), Some(DispatchExtensibility::Open)),
26749                ("send".to_string(), Some(DispatchExtensibility::Open)),
26750                ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
26751                ("Plain::send".to_string(), None),
26752            ],
26753            "the class body states the fact; a qualified definition states none"
26754        );
26755    }
26756
26757    #[test]
26758    #[hardwon(
26759        "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
26760    )]
26761    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
26762        const DISTINCT_PER_KIND: usize = 64;
26763        let mut source = String::new();
26764        for index in 0..DISTINCT_PER_KIND {
26765            writeln!(source, "typedef int Alias{index};").unwrap();
26766        }
26767        writeln!(source, "typedef long Alias0;").unwrap();
26768        for index in 0..DISTINCT_PER_KIND {
26769            writeln!(source, "#define MACRO_{index} {index}").unwrap();
26770        }
26771        writeln!(source, "#define MACRO_0 duplicate").unwrap();
26772        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
26773
26774        let mut parser = tree_sitter::Parser::new();
26775        parser
26776            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26777            .unwrap();
26778        let tree = parser.parse(&source, None).unwrap();
26779        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
26780
26781        start_declaration_identity_comparison_probe();
26782        let parsed = parse_cpp_file(&file, &source, &tree);
26783        let comparisons = finish_declaration_identity_comparison_probe();
26784
26785        assert_eq!(
26786            DISTINCT_PER_KIND + 1,
26787            parsed
26788                .declarations()
26789                .iter()
26790                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
26791                .count(),
26792            "every physical typedef alias declaration must be retained so \
26793             conditional branch guards stay available to the resolver"
26794        );
26795        assert_eq!(
26796            DISTINCT_PER_KIND + 1,
26797            parsed
26798                .declarations()
26799                .iter()
26800                .filter(|unit| {
26801                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
26802                })
26803                .count(),
26804            "distinct macro redefinitions must remain available to temporal lookup"
26805        );
26806        assert_eq!(
26807            2,
26808            parsed
26809                .declarations()
26810                .iter()
26811                .filter(|unit| {
26812                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
26813                })
26814                .count(),
26815            "function overloads must remain distinct"
26816        );
26817
26818        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
26819        assert!(
26820            comparisons <= dedup_inputs * 4,
26821            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
26822        );
26823    }
26824
26825    #[test]
26826    #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
26827    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
26828        let negative_source = r#"namespace absl {
26829ABSL_NAMESPACE_BEGIN namespace container_internal {
26830template <typename T>
26831class broken {
26832 public:
26833  using value_type = T;
26834  T operator->() const { return &operator*(); }
26835}
26836}
26837"#;
26838        let mut parser = tree_sitter::Parser::new();
26839        parser
26840            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26841            .unwrap();
26842        let negative_tree = parser.parse(negative_source, None).unwrap();
26843        let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
26844            .expect("the negative fixture must expose the malformed class node");
26845        assert!(
26846            broken.has_error(),
26847            "the negative fixture must retain a parser error"
26848        );
26849        // The macro-declaration grammar keeps the class body closed here; the
26850        // damage is the missing `;` and the unclosed namespace around it, so
26851        // the class's incompleteness is proven by the sentinel recognizers
26852        // finding no envelope for it rather than by a shared parent boundary.
26853        let mut envelope = None;
26854        {
26855            let mut stack = vec![negative_tree.root_node()];
26856            while let Some(node) = stack.pop() {
26857                if cpp_nested_namespace_sentinel(
26858                    node,
26859                    negative_source,
26860                    &ParentIndex::new(negative_tree.root_node()),
26861                )
26862                .is_some()
26863                    || cpp_root_namespace_sentinel(
26864                        node,
26865                        negative_source,
26866                        &ParentIndex::new(negative_tree.root_node()),
26867                    )
26868                    .is_some()
26869                    || cpp_sentinel_macro_class_region(node, negative_source).is_some()
26870                {
26871                    envelope = Some(node);
26872                    break;
26873                }
26874                stack.extend(node.named_children(&mut node.walk()));
26875            }
26876        }
26877        assert!(
26878            envelope.is_none(),
26879            "an incomplete class must not prove a sentinel envelope: {}",
26880            negative_tree.root_node().to_sexp()
26881        );
26882        let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
26883        assert!(
26884            recovered
26885                .iter()
26886                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
26887            "an incomplete class must not borrow the namespace close: {recovered:#?}"
26888        );
26889
26890        let positive_source = r#"namespace absl {
26891ABSL_NAMESPACE_BEGIN namespace container_internal {
26892template <typename T>
26893class broken {
26894 public:
26895  using value_type = T;
26896  T operator->() const { return &operator*(); }
26897  using alias = value_type;
26898};
26899}
26900}
26901"#;
26902        let positive_tree = parser.parse(positive_source, None).unwrap();
26903        let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
26904            .expect("the positive fixture must expose the broken class node");
26905        assert!(
26906            broken.has_error(),
26907            "the positive fixture must retain an internal parser error"
26908        );
26909        assert!(
26910            cpp_complete_class_body_close(broken).is_some(),
26911            "the positive fixture must expose a real class body close"
26912        );
26913        let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
26914        assert!(
26915            recovered.iter().any(|class| {
26916                class.scope_components == ["absl", "container_internal", "broken"]
26917            }),
26918            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
26919        );
26920    }
26921
26922    #[test]
26923    #[hardwon(
26924        "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
26925    )]
26926    fn sentinel_recovery_keeps_members_after_nested_body_close() {
26927        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
26928NLOHMANN_BASIC_JSON_TPL_DECLARATION
26929class basic_json {
26930 private:
26931  union storage {
26932    int value;
26933  } data;
26934 public:
26935  using late_alias = int;
26936  late_alias value() const;
26937};
26938NLOHMANN_JSON_NAMESPACE_END
26939"#;
26940        let mut parser = tree_sitter::Parser::new();
26941        parser
26942            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26943            .unwrap();
26944        let tree = parser.parse(source, None).unwrap();
26945        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26946        let basic_json = recovered
26947            .iter()
26948            .find(|class| {
26949                class
26950                    .scope_components
26951                    .last()
26952                    .is_some_and(|name| name == "basic_json")
26953            })
26954            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
26955        let late_alias = source
26956            .find("late_alias value")
26957            .expect("late alias reference");
26958        assert!(
26959            basic_json.class_range.start_byte < late_alias
26960                && late_alias < basic_json.class_range.end_byte,
26961            "the recovered class range must include members after a nested close: {basic_json:#?}"
26962        );
26963    }
26964
26965    #[test]
26966    #[hardwon(
26967        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
26968    )]
26969    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
26970        let source = r#"namespace absl {
26971ABSL_NAMESPACE_BEGIN namespace container_internal {
26972template <typename T>
26973struct broken {
26974  using value_type = T;
26975};
26976}
26977
26978#ifdef OWNER_DEF
26979template <typename T>
26980typename broken<T>::value_type broken<T>::method() {
26981  value_type value{};
26982  return value;
26983}
26984#endif
26985
26986namespace sibling {
26987template <typename T>
26988typename broken<T>::value_type broken<T>::other() {
26989  value_type value{};
26990  return value;
26991}
26992}
26993
26994ABSL_NAMESPACE_END
26995}
26996"#;
26997        let mut parser = tree_sitter::Parser::new();
26998        parser
26999            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27000            .unwrap();
27001        let tree = parser.parse(source, None).unwrap();
27002        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
27003        let broken = recovered
27004            .iter()
27005            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
27006            .expect("the sentinel class must be recovered");
27007        let method_start = source
27008            .find("typename broken<T>::value_type broken<T>::method()")
27009            .expect("guarded sibling owner");
27010        let method_end = source[method_start..]
27011            .find("\n}")
27012            .map(|offset| method_start + offset + 2)
27013            .expect("guarded sibling owner close");
27014        assert!(
27015            broken
27016                .owner_ranges
27017                .iter()
27018                .any(|owner| owner.range.start_byte <= method_start
27019                    && method_end <= owner.range.end_byte),
27020            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
27021        );
27022        let sibling_start = source
27023            .find("typename broken<T>::value_type broken<T>::other()")
27024            .expect("nested namespace sibling owner");
27025        assert!(
27026            broken
27027                .owner_ranges
27028                .iter()
27029                .all(|owner| owner.range.start_byte > sibling_start
27030                    || owner.range.end_byte <= sibling_start),
27031            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
27032        );
27033    }
27034
27035    #[test]
27036    #[hardwon(
27037        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
27038    )]
27039    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
27040        let source = r#"#ifdef OUTER
27041namespace absl {
27042ABSL_NAMESPACE_BEGIN namespace container_internal {
27043template <typename T>
27044struct broken {
27045  using value_type = T;
27046};
27047}
27048}
27049
27050#ifdef OWNER_DEF
27051template <typename T>
27052typename broken<T>::value_type broken<T>::method() {
27053  value_type value{};
27054  return value;
27055}
27056#endif
27057#endif
27058"#;
27059        // The macro-declaration grammar keeps the sentinel envelope clean, so
27060        // the recovered-region index has nothing malformed to repair; the
27061        // declaration walk owns the recovery, and the out-of-line definition
27062        // after the missing ABSL_NAMESPACE_END must stay unowned.
27063        let parsed = parse_cpp_declarations(source, "discard-outer.h");
27064        assert!(
27065            parsed
27066                .declarations()
27067                .iter()
27068                .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
27069            "the sentinel class must be recovered: {:#?}",
27070            parsed.declarations()
27071        );
27072        let method = parsed
27073            .declarations()
27074            .iter()
27075            .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
27076            .expect("the guarded out-of-line definition");
27077        assert_eq!(
27078            method.fq_name(),
27079            "broken.method",
27080            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
27081            parsed.declarations()
27082        );
27083    }
27084
27085    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
27086    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
27087        let mut signatures = all_function_identities(parsed)
27088            .into_iter()
27089            .filter(|(name, _)| name == fq_name)
27090            .filter_map(|(_, signature)| signature)
27091            .collect::<Vec<_>>();
27092        signatures.sort();
27093        signatures.dedup();
27094        signatures
27095    }
27096
27097    #[test]
27098    #[hardwon(
27099        "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
27100    )]
27101    fn callable_parameter_types_come_from_the_ast_parameter_list() {
27102        let source = r#"
27103template <typename T, ENABLE_BYTES(T)>
27104Vec256<T> DupOdd(Vec256<T> value) { return value; }
27105
27106struct Visitor {
27107  void fail(this auto const& self) {}
27108  void consume(this Visitor const& self, int count) {}
27109};
27110"#;
27111        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
27112        let dup_odd = parsed
27113            .declarations()
27114            .iter()
27115            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
27116            .expect("DupOdd declaration");
27117        assert_eq!(
27118            dup_odd.signature(),
27119            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
27120        );
27121        assert_eq!(
27122            parsed
27123                .signature_metadata
27124                .get(dup_odd)
27125                .and_then(|metadata| metadata.first())
27126                .and_then(SignatureMetadata::callable_parameter_types),
27127            Some(["Vec256<T>".to_string()].as_slice())
27128        );
27129
27130        let fail = parsed
27131            .declarations()
27132            .iter()
27133            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
27134            .expect("explicit-object member");
27135        assert_eq!(fail.signature(), Some("(this const auto &)"));
27136        let metadata = parsed
27137            .signature_metadata
27138            .get(fail)
27139            .and_then(|metadata| metadata.first())
27140            .expect("explicit-object signature metadata");
27141        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
27142        assert!(
27143            metadata
27144                .callable_arity()
27145                .is_some_and(|arity| arity.accepts(0))
27146        );
27147        let consume = parsed
27148            .declarations()
27149            .iter()
27150            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
27151            .expect("explicit-object member with an invocation argument");
27152        assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
27153        let metadata = parsed
27154            .signature_metadata
27155            .get(consume)
27156            .and_then(|metadata| metadata.first())
27157            .expect("typed explicit-object signature metadata");
27158        assert_eq!(
27159            metadata.callable_parameter_types(),
27160            Some(["int".to_string()].as_slice())
27161        );
27162        assert!(
27163            metadata
27164                .callable_arity()
27165                .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
27166        );
27167    }
27168
27169    #[test]
27170    #[hardwon(
27171        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
27172    )]
27173    fn trailing_qualifiers_survive_parameter_list_whitespace() {
27174        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
27175        // declarator's structure, so an out-of-line definition that spells its
27176        // parameter list with different whitespace than the declaration must
27177        // still carry it.
27178        let source = r#"
27179struct Widget {
27180  bool multiline(int settings, int supprs) const;
27181  bool doublespace(int settings, int supprs) const;
27182  bool noexcept_multiline(int settings, int supprs) noexcept;
27183  bool ref_multiline(int settings, int supprs) &&;
27184};
27185bool
27186Widget::multiline (int settings,
27187                   int supprs) const
27188{ return settings + supprs > 0; }
27189bool Widget::doublespace(int settings,  int supprs) const { return true; }
27190bool Widget::noexcept_multiline(int settings,
27191                                int supprs) noexcept { return true; }
27192bool Widget::ref_multiline(int settings,
27193                           int supprs) && { return true; }
27194"#;
27195        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
27196        assert_eq!(
27197            vec!["(int, int) const".to_string()],
27198            identity_signatures(&parsed, "Widget.multiline")
27199        );
27200        assert_eq!(
27201            vec!["(int, int) const".to_string()],
27202            identity_signatures(&parsed, "Widget.doublespace")
27203        );
27204        assert_eq!(
27205            vec!["(int, int) noexcept".to_string()],
27206            identity_signatures(&parsed, "Widget.noexcept_multiline")
27207        );
27208        assert_eq!(
27209            vec!["(int, int) &&".to_string()],
27210            identity_signatures(&parsed, "Widget.ref_multiline")
27211        );
27212    }
27213
27214    #[test]
27215    #[hardwon(
27216        "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27217    )]
27218    fn macro_fragmented_plain_class_keeps_following_member_signature() {
27219        let source = r#"
27220struct CString {};
27221class CMessage {
27222public:
27223  CString GetParams(unsigned int index, unsigned int length = -1) const
27224      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
27225    return GetParamsColon(index, length);
27226  }
27227  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
27228};
27229CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
27230  return {};
27231}
27232"#;
27233        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
27234        assert_eq!(
27235            vec!["(unsigned int, unsigned int) const".to_string()],
27236            identity_signatures(&parsed, "CMessage.GetParamsColon")
27237        );
27238    }
27239
27240    #[test]
27241    #[hardwon(
27242        "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
27243    )]
27244    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
27245        let source = r#"
27246#pragma once
27247#define DEMO_DEPRECATED(message)
27248namespace demo {
27249struct Base {
27250    static int aligned(int value) { return value; }
27251    int legacy(int value) const
27252        DEMO_DEPRECATED("use replacement()") { return value; }
27253    int replacement() const;
27254    void run(int value);
27255};
27256struct OtherBase {
27257    void run(int value);
27258    static int aligned(int value) { return value; }
27259};
27260struct Derived : Base {};
27261struct Override : Base {
27262    void run(int value);
27263    static int aligned(int value) { return value; }
27264};
27265struct RecoveredOverride : Base {
27266    int legacy(int value) const
27267        DEMO_DEPRECATED("use replacement()") { return value; }
27268    void run(int value);
27269};
27270struct Hidden : Base {
27271    void run(int first, int second);
27272    static int aligned(int first, int second) { return first + second; }
27273};
27274struct Ambiguous : Base, OtherBase {};
27275}
27276struct Global {};
27277"#;
27278        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
27279        let declarations = parsed.declarations();
27280        let fq_names = declarations
27281            .iter()
27282            .map(|unit| unit.fq_name())
27283            .collect::<std::collections::BTreeSet<_>>();
27284
27285        for expected in [
27286            "demo.Base",
27287            "demo.Base.aligned",
27288            "demo.Base.legacy",
27289            "demo.Base.replacement",
27290            "demo.Base.run",
27291            "demo.Derived",
27292            "demo.OtherBase",
27293            "demo.Override",
27294            "demo.RecoveredOverride",
27295            "demo.Hidden",
27296            "demo.Ambiguous",
27297            "Global",
27298        ] {
27299            assert!(
27300                fq_names.contains(expected),
27301                "missing {expected} from namespaced macro fragment: {declarations:#?}"
27302            );
27303        }
27304        assert!(
27305            !fq_names.contains("Derived"),
27306            "following class escaped its namespace: {declarations:#?}"
27307        );
27308        assert!(
27309            !fq_names.contains("demo.Global"),
27310            "global class crossed the recovered namespace boundary: {declarations:#?}"
27311        );
27312    }
27313
27314    #[test]
27315    #[hardwon(
27316        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
27317    )]
27318    fn trailing_qualifiers_still_separate_genuine_overloads() {
27319        // The qualifier must keep distinguishing the real C++ overload sets it
27320        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
27321        let source = r#"
27322struct Widget {
27323  int* slot(int index);
27324  const int* slot(int index) const;
27325  int log(int severity) &;
27326  int log(int severity) &&;
27327};
27328"#;
27329        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
27330        assert_eq!(
27331            vec!["(int)".to_string(), "(int) const".to_string()],
27332            identity_signatures(&parsed, "Widget.slot")
27333        );
27334        assert_eq!(
27335            vec!["(int) &".to_string(), "(int) &&".to_string()],
27336            identity_signatures(&parsed, "Widget.log")
27337        );
27338    }
27339
27340    #[test]
27341    #[hardwon(
27342        "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
27343    )]
27344    fn virtual_specifier_is_not_part_of_the_identity_signature() {
27345        // `override` never appears on the out-of-line definition, and C++ does
27346        // not make it part of the signature, so it must not split the identity.
27347        let source = r#"
27348struct Base {
27349  virtual void run(int value) const;
27350};
27351struct Widget : Base {
27352  void run(int value) const override;
27353};
27354void Widget::run(int value) const {}
27355"#;
27356        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
27357        assert_eq!(
27358            vec!["(int) const".to_string()],
27359            identity_signatures(&parsed, "Widget.run")
27360        );
27361    }
27362
27363    #[test]
27364    #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
27365    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
27366        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
27367        // the function type, so a declaration that spells `const int` and a
27368        // definition that spells `int` are one entity.
27369        let source = r#"
27370struct Widget {
27371  bool value_params(const int settings, const int supprs);
27372  void pointee_const(const int* p);
27373  void pointer_const(int* const p);
27374  void both_const(const int* const p);
27375  void reference_const(const int& p);
27376  void array_const(const int values[4]);
27377  void take(const int* p);
27378  void take(int* p);
27379};
27380bool Widget::value_params(int settings, int supprs) { return true; }
27381void Widget::pointer_const(int* p) {}
27382void Widget::both_const(const int* p) {}
27383"#;
27384        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27385        assert_eq!(
27386            vec!["(int, int)".to_string()],
27387            identity_signatures(&parsed, "Widget.value_params")
27388        );
27389        assert_eq!(
27390            vec!["(int *)".to_string()],
27391            identity_signatures(&parsed, "Widget.pointer_const")
27392        );
27393        assert_eq!(
27394            vec!["(const int *)".to_string()],
27395            identity_signatures(&parsed, "Widget.both_const")
27396        );
27397        // The const that is not top-level still distinguishes the type.
27398        assert_eq!(
27399            vec!["(const int *)".to_string()],
27400            identity_signatures(&parsed, "Widget.pointee_const")
27401        );
27402        assert_eq!(
27403            vec!["(const int &)".to_string()],
27404            identity_signatures(&parsed, "Widget.reference_const")
27405        );
27406        assert_eq!(
27407            vec!["(const int [4])".to_string()],
27408            identity_signatures(&parsed, "Widget.array_const")
27409        );
27410        assert_eq!(
27411            vec!["(const int *)".to_string(), "(int *)".to_string()],
27412            identity_signatures(&parsed, "Widget.take")
27413        );
27414    }
27415
27416    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27417        let mut parser = tree_sitter::Parser::new();
27418        parser
27419            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27420            .unwrap();
27421        let tree = parser.parse(source, None).unwrap();
27422        let start = source.find(callable_name).expect("callable declaration");
27423        let declarator =
27424            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27425        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27426    }
27427
27428    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27429        let mut shapes = comparable_shapes(source, callable_name);
27430        assert_eq!(1, shapes.len(), "{shapes:?}");
27431        match shapes.remove(0) {
27432            CppComparableSlot::Shape(shape) => shape,
27433            other => panic!("expected a comparable shape, got {other:?}"),
27434        }
27435    }
27436
27437    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27438        let mut current = shape.root();
27439        loop {
27440            match shape.node(current) {
27441                CppComparableNode::Named { .. } => return shape.node(current),
27442                CppComparableNode::Pointer { inner, .. }
27443                | CppComparableNode::Reference { inner }
27444                | CppComparableNode::Array { inner } => current = *inner,
27445                CppComparableNode::Generic { base, .. } => current = *base,
27446            }
27447        }
27448    }
27449
27450    #[test]
27451    #[hardwon(
27452        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27453    )]
27454    fn comparable_shape_keeps_pointee_const() {
27455        assert_ne!(
27456            sole_comparable_shape("void f(const char* p);", "f("),
27457            sole_comparable_shape("void f(char* p);", "f(")
27458        );
27459    }
27460
27461    #[test]
27462    #[hardwon(
27463        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27464    )]
27465    fn comparable_shape_keeps_inner_pointer_const() {
27466        assert_ne!(
27467            sole_comparable_shape("void f(int** p);", "f("),
27468            sole_comparable_shape("void f(int* const* p);", "f(")
27469        );
27470    }
27471
27472    #[test]
27473    #[hardwon(
27474        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27475    )]
27476    fn comparable_shape_drops_top_level_pointer_const() {
27477        assert_eq!(
27478            sole_comparable_shape("void f(int* const p);", "f("),
27479            sole_comparable_shape("void f(int* p);", "f(")
27480        );
27481    }
27482
27483    #[test]
27484    #[hardwon(
27485        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27486    )]
27487    fn comparable_shape_drops_top_level_base_const() {
27488        assert_eq!(
27489            sole_comparable_shape("void f(const int p);", "f("),
27490            sole_comparable_shape("void f(int p);", "f(")
27491        );
27492    }
27493
27494    #[test]
27495    #[hardwon(
27496        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27497    )]
27498    fn comparable_shape_decays_top_level_array_to_pointer() {
27499        assert_eq!(
27500            sole_comparable_shape("void f(int a[3]);", "f("),
27501            sole_comparable_shape("void f(int* a);", "f(")
27502        );
27503        assert_eq!(
27504            sole_comparable_shape("void f(int* a[3]);", "f("),
27505            sole_comparable_shape("void f(int** a);", "f(")
27506        );
27507    }
27508
27509    #[test]
27510    #[hardwon(
27511        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27512    )]
27513    fn comparable_shape_keeps_array_behind_pointer() {
27514        assert_ne!(
27515            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27516            sole_comparable_shape("struct S { void f(int** a); };", "f(")
27517        );
27518    }
27519
27520    #[test]
27521    #[hardwon(
27522        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27523    )]
27524    fn comparable_shape_records_written_name_and_lexical_scope() {
27525        let declared =
27526            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27527        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27528        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27529            panic!("named leaf");
27530        };
27531        assert_eq!(["Msg".to_string()].as_slice(), name.path());
27532        assert_eq!(
27533            ["ns".to_string(), "S".to_string()].as_slice(),
27534            name.lexical_scope()
27535        );
27536        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27537            panic!("named leaf");
27538        };
27539        assert_eq!(
27540            ["ns".to_string(), "Msg".to_string()].as_slice(),
27541            name.path()
27542        );
27543        assert!(name.lexical_scope().is_empty());
27544        assert_ne!(declared, defined);
27545    }
27546
27547    #[test]
27548    #[hardwon(
27549        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27550    )]
27551    fn comparable_shape_marks_sized_primitive_leaf() {
27552        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27553        let CppComparableNode::Named {
27554            name, primitive, ..
27555        } = comparable_named_leaf(&shape)
27556        else {
27557            panic!("named leaf");
27558        };
27559        assert!(primitive);
27560        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
27561        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
27562    }
27563
27564    #[test]
27565    #[hardwon(
27566        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
27567    )]
27568    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
27569        assert_eq!(
27570            vec![CppComparableSlot::Unstructured],
27571            comparable_shapes("void f(void (*cb)(int));", "f(")
27572        );
27573    }
27574
27575    #[test]
27576    #[hardwon(
27577        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
27578    )]
27579    fn comparable_shape_reports_ellipsis_slot() {
27580        let shapes = comparable_shapes("void f(int a, ...);", "f(");
27581        assert_eq!(2, shapes.len(), "{shapes:?}");
27582        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
27583    }
27584
27585    #[test]
27586    #[hardwon(
27587        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
27588    )]
27589    fn comparable_shape_keeps_template_argument_const() {
27590        assert_ne!(
27591            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
27592            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
27593        );
27594    }
27595
27596    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
27597    /// file-scope tag that a later `struct inner *` at file scope may name.
27598    #[test]
27599    #[hardwon(
27600        "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
27601    )]
27602    fn c_file_mints_aggregate_member_tag_at_file_scope() {
27603        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27604        let parsed = parse_cpp_declarations(source, "x.c");
27605        let declarations = parsed.declarations();
27606
27607        assert!(
27608            declarations
27609                .iter()
27610                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27611            "expected a file-scope inner tag, got {declarations:?}"
27612        );
27613        assert!(
27614            declarations
27615                .iter()
27616                .all(|unit| unit.fq_name() != "outer$inner"),
27617            "expected no nested identity, got {declarations:?}"
27618        );
27619        assert!(
27620            declarations
27621                .iter()
27622                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
27623        );
27624        // Members still belong to their own aggregate.
27625        assert!(
27626            declarations
27627                .iter()
27628                .any(|unit| unit.fq_name() == "inner.value")
27629        );
27630        assert!(
27631            declarations
27632                .iter()
27633                .any(|unit| unit.fq_name() == "outer.item")
27634        );
27635
27636        let outer = declarations
27637            .iter()
27638            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
27639            .expect("outer");
27640        assert!(
27641            parsed
27642                .children
27643                .get(outer)
27644                .into_iter()
27645                .flatten()
27646                .all(|child| child.fq_name() != "inner"),
27647            "the tag must not hang off the aggregate it is written inside: {:?}",
27648            parsed.children
27649        );
27650    }
27651
27652    /// A header carries no compilation language of its own, and a `.cpp`
27653    /// translation unit really does declare a nested class. Both keep exactly
27654    /// the C++ extraction they had before the C dialect existed.
27655    #[test]
27656    #[hardwon(
27657        "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
27658    )]
27659    fn header_and_cpp_files_keep_nested_tag_identity() {
27660        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27661        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
27662            let parsed = parse_cpp_declarations(source, name);
27663            let declarations = parsed.declarations();
27664            assert!(
27665                declarations
27666                    .iter()
27667                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27668                "{name} must keep the nested identity, got {declarations:?}"
27669            );
27670            assert!(
27671                declarations.iter().all(|unit| unit.fq_name() != "inner"),
27672                "{name} must not mint a file-scope tag, got {declarations:?}"
27673            );
27674            assert!(
27675                declarations
27676                    .iter()
27677                    .any(|unit| unit.fq_name() == "outer$inner.value")
27678            );
27679        }
27680    }
27681
27682    #[test]
27683    fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
27684        let source = r#"
27685template <class K, class V, class Traits = CacheTraits<K>>
27686class ARCCache {
27687 public:
27688    struct ArcSizeInfo {
27689        void BSize() {}
27690    };
27691 private:
27692    struct BMapVal;
27693    struct BListVal;
27694    typedef std::unordered_map<K, BMapVal> BMap;
27695    typedef typename BMap::iterator bmap_iter;
27696    typedef typename std::list<BListVal> BList;
27697    typedef typename BList::iterator blist_iter;
27698    struct BListVal {
27699        bmap_iter map_iter;
27700        BListVal() = default;
27701    };
27702    struct DefinedVal {
27703        DefinedVal() {}
27704    };
27705};
27706"#;
27707        let tree = crate::diagnostics::parse_cpp_tree(source);
27708        let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
27709        let cpp = crate::adapter::parse_cpp_file_in_dialect(
27710            &file,
27711            source,
27712            &tree,
27713            LanguageDialect::Standard(Language::Cpp),
27714        );
27715        let c =
27716            crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
27717        for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
27718            for fq_name in [
27719                "ARCCache$ArcSizeInfo.BSize",
27720                "ARCCache$BListVal.BListVal",
27721                "ARCCache$DefinedVal.DefinedVal",
27722            ] {
27723                assert!(
27724                    parsed
27725                        .declarations()
27726                        .iter()
27727                        .any(|unit| unit.fq_name() == fq_name),
27728                    "{dialect} reading must contain {fq_name}: {:#?}",
27729                    parsed.declarations()
27730                );
27731            }
27732            for flattened in [
27733                "ArcSizeInfo.BSize",
27734                "BListVal.BListVal",
27735                "DefinedVal.DefinedVal",
27736            ] {
27737                assert!(
27738                    parsed
27739                        .declarations()
27740                        .iter()
27741                        .all(|unit| unit.fq_name() != flattened),
27742                    "{dialect} reading must not flatten {flattened}: {:#?}",
27743                    parsed.declarations()
27744                );
27745            }
27746        }
27747    }
27748
27749    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
27750    #[test]
27751    #[hardwon(
27752        "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
27753    )]
27754    fn uppercase_c_extension_keeps_cpp_tag_scope() {
27755        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27756        let parsed = parse_cpp_declarations(source, "x.C");
27757        assert!(
27758            parsed
27759                .declarations()
27760                .iter()
27761                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
27762        );
27763    }
27764
27765    /// There is no such thing as a partially nested tag in C: every level of a
27766    /// nested aggregate chain lands at the same enclosing scope.
27767    #[test]
27768    #[hardwon(
27769        "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
27770    )]
27771    fn c_file_mints_every_nesting_level_at_file_scope() {
27772        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
27773        let parsed = parse_cpp_declarations(source, "z.c");
27774        let declarations = parsed.declarations();
27775
27776        for tag in ["a", "b", "c"] {
27777            assert!(
27778                declarations
27779                    .iter()
27780                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
27781                "expected a file-scope {tag}, got {declarations:?}"
27782            );
27783        }
27784        assert!(
27785            declarations
27786                .iter()
27787                .all(|unit| !unit.fq_name().contains('$')),
27788            "no level may keep a nested identity, got {declarations:?}"
27789        );
27790        // Each member still belongs to the aggregate that declares it.
27791        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
27792        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
27793        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
27794    }
27795
27796    /// An enum tag is a tag; its enumerators stay members of the enum, which is
27797    /// what makes them ordinary identifiers at the enum's own (file) scope.
27798    #[test]
27799    #[hardwon(
27800        "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
27801    )]
27802    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
27803        let source = "struct outer { enum color { RED, GREEN } c; };\n";
27804        let parsed = parse_cpp_declarations(source, "e.c");
27805        let declarations = parsed.declarations();
27806
27807        let color = declarations
27808            .iter()
27809            .find(|unit| unit.is_class() && unit.fq_name() == "color")
27810            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
27811        assert!(
27812            declarations
27813                .iter()
27814                .all(|unit| unit.fq_name() != "outer$color")
27815        );
27816        for enumerator in ["color.RED", "color.GREEN"] {
27817            assert!(
27818                declarations.iter().any(|unit| unit.fq_name() == enumerator),
27819                "expected {enumerator}, got {declarations:?}"
27820            );
27821        }
27822        let children = parsed
27823            .children
27824            .get(color)
27825            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
27826        assert!(
27827            ["color.RED", "color.GREEN"]
27828                .iter()
27829                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
27830            "enumerators must hang off their enum: {children:?}"
27831        );
27832    }
27833
27834    #[test]
27835    #[hardwon(
27836        "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
27837    )]
27838    fn c_file_mints_member_list_union_at_file_scope() {
27839        let source = "struct outer { union inner { int a; float b; } item; };\n";
27840        let parsed = parse_cpp_declarations(source, "u.c");
27841        let declarations = parsed.declarations();
27842        assert!(
27843            declarations
27844                .iter()
27845                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27846            "expected a file-scope inner union, got {declarations:?}"
27847        );
27848        assert!(
27849            declarations
27850                .iter()
27851                .all(|unit| unit.fq_name() != "outer$inner")
27852        );
27853        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
27854        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
27855    }
27856
27857    /// A tag declared in a namespace member list is not a file-scope tag: the
27858    /// nearest enclosing non-aggregate scope is the namespace.
27859    #[test]
27860    #[hardwon(
27861        "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
27862    )]
27863    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
27864        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
27865        let parsed = parse_cpp_declarations(source, "n.c");
27866        let declarations = parsed.declarations();
27867        let inner = declarations
27868            .iter()
27869            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
27870            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
27871        assert_eq!(inner.package_name(), "ns");
27872        assert!(
27873            declarations
27874                .iter()
27875                .all(|unit| unit.fq_name() != "ns.outer$inner")
27876        );
27877    }
27878
27879    /// A tag declared inside a function body: the C reading mints nothing for
27880    /// it (C block scope is a separate capability), while the C++ reading
27881    /// mints the local class under its block identity and its nested class
27882    /// under the local class (#6902). Neither reading flattens either tag into
27883    /// file scope.
27884    #[test]
27885    #[hardwon(
27886        "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
27887    )]
27888    fn function_local_tags_keep_block_scope_in_both_dialects() {
27889        let source =
27890            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
27891        for (name, local) in [("y.c", false), ("y.cpp", true)] {
27892            let parsed = parse_cpp_declarations(source, name);
27893            let declarations = parsed.declarations();
27894            assert!(
27895                declarations
27896                    .iter()
27897                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
27898                "{name}: {declarations:?}"
27899            );
27900            for tag in ["localtag", "deeper", "localtag$deeper"] {
27901                assert!(
27902                    declarations.iter().all(|unit| unit.fq_name() != tag),
27903                    "{name} must not mint {tag}, got {declarations:?}"
27904                );
27905            }
27906            let callable = declarations
27907                .iter()
27908                .find(|unit| unit.is_function() && unit.fq_name() == "run")
27909                .unwrap();
27910            let block = format!("run$owner${}$block$0", callable.declaration_id());
27911            for tag in [
27912                format!("{block}$localtag"),
27913                format!("{block}$localtag$deeper"),
27914                format!("{block}$localtag.d"),
27915            ] {
27916                assert_eq!(
27917                    declarations.iter().any(|unit| unit.fq_name() == tag),
27918                    local,
27919                    "{name}: {tag} in {declarations:?}"
27920                );
27921            }
27922        }
27923    }
27924
27925    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
27926    /// re-scope: the typedef name is identical in both dialects.
27927    #[test]
27928    #[hardwon(
27929        "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
27930    )]
27931    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
27932        let source = "typedef struct { int v; } T;\n";
27933        for name in ["t.c", "t.cpp"] {
27934            let parsed = parse_cpp_declarations(source, name);
27935            let declarations = parsed.declarations();
27936            assert!(
27937                declarations
27938                    .iter()
27939                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
27940                "{name}: {declarations:?}"
27941            );
27942        }
27943    }
27944
27945    /// The same principle for every anonymous aggregate the walk indexes, not
27946    /// just the typedef one: a promoted anonymous union in a member list, an
27947    /// anonymous struct given a receiver type by its declarator, a
27948    /// function-local aggregate, and one inside an inline member function
27949    /// body. The C++ reading used to mint none of these, so a pure C++
27950    /// workspace indexed no members at all (#3301).
27951    #[test]
27952    #[hardwon(
27953        "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
27954    )]
27955    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
27956        let sources = [
27957            "struct outer { union { int a; float b; }; };\n",
27958            "struct outer { struct { int *ops; } sock; };\n",
27959            "void run(void) { struct { int v; } item; }\n",
27960            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
27961        ];
27962        for source in sources {
27963            let names = |name: &str| {
27964                parse_cpp_declarations(source, name)
27965                    .declarations()
27966                    .iter()
27967                    .map(|unit| (format!("{:?}", unit.kind()), unit.identifier().to_owned()))
27968                    .collect::<std::collections::BTreeSet<_>>()
27969            };
27970            let c_names = names("a.c");
27971            let cpp_names = names("a.cpp");
27972            assert_eq!(
27973                c_names, cpp_names,
27974                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
27975            );
27976            assert!(
27977                c_names.iter().any(|(kind, _)| kind == "Field"),
27978                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
27979            );
27980        }
27981    }
27982
27983    #[test]
27984    #[hardwon(
27985        "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
27986    )]
27987    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
27988        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
27989        let parsed = parse_cpp_declarations(source, "socket.c");
27990        let declarations = parsed.declarations();
27991        assert_eq!(
27992            declarations
27993                .iter()
27994                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
27995                .count(),
27996            1,
27997            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
27998        );
27999        for expected in [
28000            "PAL_HANDLE",
28001            "PAL_HANDLE.sock",
28002            "PAL_HANDLE$sock",
28003            "PAL_HANDLE$sock.ops",
28004        ] {
28005            assert!(
28006                declarations.iter().any(|unit| unit.fq_name() == expected),
28007                "expected {expected}, got {declarations:?}"
28008            );
28009        }
28010    }
28011
28012    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
28013    /// every (node, class-like name) pair a tree offers, and require the same
28014    /// answer from both.
28015    ///
28016    /// The release build has no `debug_assertions` agreement check, so this is
28017    /// what pins the two together there.  Both query orders are exercised:
28018    /// document order is what the walk does, and reverse order proves that a
28019    /// question about an earlier byte than one already answered is still
28020    /// filtered back to its own prefix rather than answered from the wider
28021    /// fold.
28022    ///
28023    /// Returns how many questions were answered with a namespace, so a fixture
28024    /// can assert it actually reached the path (#2754).
28025    fn namespace_forward_scan_agreement(source: &str) -> usize {
28026        let mut parser = tree_sitter::Parser::new();
28027        parser
28028            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28029            .unwrap();
28030        let tree = parser.parse(source, None).unwrap();
28031        let root = tree.root_node();
28032        let ancestry = ParentIndex::new(root);
28033
28034        let mut nodes = Vec::new();
28035        let mut names = std::collections::BTreeSet::new();
28036        let mut cursor = root.walk();
28037        let mut stack = vec![root];
28038        while let Some(node) = stack.pop() {
28039            if matches!(
28040                node.kind(),
28041                "class_specifier" | "struct_specifier" | "union_specifier"
28042            ) && let Some(name) = class_like_name(node, source, &ancestry)
28043            {
28044                names.insert(name);
28045            }
28046            nodes.push(node);
28047            stack.extend(node.named_children(&mut cursor));
28048        }
28049        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
28050        assert!(!names.is_empty(), "fixture declares no class-like name");
28051
28052        let mut answered = 0usize;
28053        for reversed in [false, true] {
28054            let mut scan = CppNamespaceForwardScan::default();
28055            let ordered: Vec<_> = if reversed {
28056                nodes.iter().rev().copied().collect()
28057            } else {
28058                nodes.clone()
28059            };
28060            answered = 0;
28061            for node in ordered {
28062                for name in &names {
28063                    scan.advance_to(root, node.start_byte(), source, &ancestry);
28064                    let carried = scan.unique_earlier_forward(name, node);
28065                    assert_eq!(
28066                        carried,
28067                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
28068                        "carried-forward scan and prefix scan disagree about {name} at \
28069                         {} node starting at byte {} (reversed order: {reversed})",
28070                        node.kind(),
28071                        node.start_byte()
28072                    );
28073                    answered += usize::from(carried.is_some());
28074                }
28075            }
28076        }
28077        answered
28078    }
28079
28080    /// A malformed namespace whose forward declarations are the only identity
28081    /// signal left for the class definitions tree-sitter pushed out to file
28082    /// scope.  Both recovered classes open the guard; only the first one is
28083    /// separated from the namespace by nothing but recovery trivia, so only the
28084    /// first one borrows.  The carried-forward scan has to reproduce both
28085    /// answers.
28086    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
28087namespace ns {
28088class Widget;
28089class Gadget;
28090int x = ;
28091}
28092class API Widget {
28093public:
28094    void first();
28095};
28096class API Gadget {
28097public:
28098    void second();
28099};
28100"#;
28101
28102    #[test]
28103    #[hardwon(
28104        "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
28105    )]
28106    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
28107        assert!(
28108            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
28109            "the fixture must actually reach the namespace-borrow path"
28110        );
28111
28112        // Nothing here may be answered, and the two paths have to agree about
28113        // that too: a clean namespace is not an identity proof, two forwards of
28114        // one name are ambiguous rather than a guess, and a forward inside a
28115        // function body is not at namespace scope.
28116        for source in [
28117            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
28118            r#"#define API
28119namespace ns {
28120class Widget;
28121class Widget;
28122int x = ;
28123}
28124class API Widget {
28125public:
28126    void method();
28127};
28128"#,
28129            r#"#define API
28130namespace ns {
28131void host() {
28132    class Widget;
28133}
28134int x = ;
28135}
28136class API Widget {
28137public:
28138    void method();
28139};
28140"#,
28141        ] {
28142            assert_eq!(
28143                namespace_forward_scan_agreement(source),
28144                0,
28145                "no borrow is justified here: {source}"
28146            );
28147        }
28148    }
28149
28150    /// Drive the region reparse and the whitespace-padded reparse it replaced
28151    /// over the same region and require identical trees.
28152    ///
28153    /// The release build has no `debug_assertions` agreement check, so this is
28154    /// what pins the two together there. Each body is reparsed at its own
28155    /// offset and again after a long prefix, because the prefix is the whole
28156    /// difference between the two techniques: the padded parse lexes it as
28157    /// whitespace, the included-range parse never sees it, and the tree has to
28158    /// come out the same either way (#2788).
28159    fn fragmented_class_reparse_agreement(body: &str) {
28160        for prefix in [
28161            String::new(),
28162            "// leading comment\n".to_string(),
28163            // A body starts just after its class head's `{`, which is normally
28164            // mid-line: the padded parse then has spaces before the region on
28165            // the region's own line, and the included-range parse has nothing
28166            // at all before it.
28167            "class Widget : public Base { ".to_string(),
28168            "namespace filler {\n".to_string()
28169                + &"struct Filler { int member; };\n".repeat(200)
28170                + "}\n",
28171            "namespace filler {\n".to_string()
28172                + &"struct Filler { int member; };\n".repeat(200)
28173                + "}\nclass Widget : public Base { ",
28174        ] {
28175            let source = format!("{prefix}{body}");
28176            let start = prefix.len();
28177            let end = source.len();
28178            let region = cpp_reparse_fragmented_class_body(&source, start, end)
28179                .expect("the region reparse must produce a tree");
28180            let padded = cpp_reparse_padded_class_body(&source, start, end)
28181                .expect("the padded reparse must produce a tree");
28182            assert_eq!(
28183                cpp_tree_shape(&region),
28184                cpp_tree_shape(&padded),
28185                "region and padded reparse disagree at offset {start} of {end} bytes"
28186            );
28187            assert_eq!(
28188                region.root_node().start_byte(),
28189                start,
28190                "the reparsed region keeps its original offsets"
28191            );
28192        }
28193    }
28194
28195    #[test]
28196    #[hardwon(
28197        "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
28198    )]
28199    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
28200        // A conditional immediately after an access label: the shape the padded
28201        // technique was kept for, because the directive and its macro name
28202        // become an ERROR plus the following declaration's apparent type.
28203        fragmented_class_reparse_agreement(
28204            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
28205        );
28206        fragmented_class_reparse_agreement(
28207            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
28208        );
28209        // The merged inline constructor and the nested fragmented bodies the
28210        // #938 recovery reads out of a reparse.
28211        fragmented_class_reparse_agreement(
28212            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
28213        );
28214        fragmented_class_reparse_agreement(
28215            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
28216        );
28217        // A body that is not member-shaped at all still has to reparse the same
28218        // way, because the admission gate reads the tree to reject it.
28219        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
28220    }
28221
28222    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
28223        let mut siblings = Vec::new();
28224        let mut anchor = node;
28225        while let Some(parent) = anchor.parent() {
28226            let at_translation_unit = parent.kind() == "translation_unit";
28227            let mut sibling = anchor.next_named_sibling();
28228            while let Some(current) = sibling {
28229                if at_translation_unit
28230                    && (current.kind() == "namespace_definition"
28231                        || (current.kind() == "function_definition"
28232                            && first_class_like_child(current).is_some()))
28233                {
28234                    return siblings;
28235                }
28236                siblings.push(current);
28237                if cpp_is_stray_close_brace(current, source) {
28238                    if let Some(semicolon) = current
28239                        .next_named_sibling()
28240                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
28241                    {
28242                        siblings.push(semicolon);
28243                    }
28244                    return siblings;
28245                }
28246                if current.start_byte() >= node.end_byte()
28247                    && matches!(current.kind(), "ERROR" | "labeled_statement")
28248                    && cpp_nested_stray_close_brace(current, source).is_some()
28249                {
28250                    return siblings;
28251                }
28252                sibling = current.next_named_sibling();
28253            }
28254            anchor = parent;
28255        }
28256        siblings
28257    }
28258
28259    #[test]
28260    #[hardwon(
28261        "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
28262    )]
28263    fn following_named_siblings_cursor_matches_native_traversal() {
28264        // A damaged export-class head scatters members across container
28265        // siblings, and the anonymous punctuation forces the cursor to filter
28266        // children exactly as Node::next_named_sibling does. The grammar reads
28267        // a function-like export macro head since brokk-tree-sitter-cpp
28268        // 0.24.7, so the fixture uses an object-like macro before a base
28269        // clause, which still has no parse.
28270        let source = r#"
28271namespace api {
28272class PROJECT_API Widget : public virtual Base {
28273  public:
28274    Widget() {}
28275    void first();
28276};
28277enum After { Value };
28278}
28279"#;
28280        let mut parser = tree_sitter::Parser::new();
28281        parser
28282            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28283            .unwrap();
28284        let tree = parser.parse(source, None).unwrap();
28285        let root = tree.root_node();
28286        assert!(root.has_error(), "fixture must remain damaged");
28287
28288        let ancestry = ParentIndex::new(root);
28289        let mut stack = vec![root];
28290        let mut checked = 0;
28291        let mut damaged = 0;
28292        let mut anonymous = 0;
28293        let mut nonempty_sequences = 0;
28294        let mut named_then_anonymous = 0;
28295        while let Some(node) = stack.pop() {
28296            let expected = native_following_named_siblings(node, source)
28297                .iter()
28298                .map(Node::id)
28299                .collect::<Vec<_>>();
28300            let actual = cpp_following_named_siblings(node, source, &ancestry)
28301                .iter()
28302                .map(Node::id)
28303                .collect::<Vec<_>>();
28304            assert_eq!(
28305                actual,
28306                expected,
28307                "cursor traversal diverged at byte {}",
28308                node.start_byte()
28309            );
28310
28311            let mut cursor = node.walk();
28312            let children = node.children(&mut cursor).collect::<Vec<_>>();
28313            stack.extend(children.iter().copied());
28314            checked += 1;
28315            damaged += usize::from(node.is_error() || node.is_missing());
28316            anonymous += usize::from(!node.is_named());
28317            nonempty_sequences += usize::from(!actual.is_empty());
28318            named_then_anonymous += usize::from(
28319                children
28320                    .windows(2)
28321                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
28322            );
28323        }
28324        assert!(checked > 0, "fixture must provide nodes to compare");
28325        assert!(damaged > 0, "fixture must cover damaged nodes");
28326        assert!(anonymous > 0, "fixture must cover anonymous nodes");
28327        assert!(
28328            nonempty_sequences > 0,
28329            "fixture must exercise following named siblings"
28330        );
28331        assert!(
28332            named_then_anonymous > 0,
28333            "fixture must exercise anonymous nodes following a named anchor"
28334        );
28335    }
28336
28337    #[test]
28338    #[hardwon(
28339        "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
28340    )]
28341    fn a_replacement_removes_owned_enum_fields() {
28342        let source =
28343            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
28344        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
28345        let mut names: Vec<_> = parsed
28346            .declarations()
28347            .iter()
28348            .map(|unit| unit.fq_name())
28349            .collect();
28350        names.sort();
28351        assert_eq!(
28352            names,
28353            vec![
28354                "Color".to_string(),
28355                "First".to_string(),
28356                "First.A".to_string()
28357            ],
28358            "the replaced Color owns no field any more"
28359        );
28360    }
28361
28362    /// A recovery that re-declares what the file already declared mints
28363    /// nothing.
28364    ///
28365    /// The reparse walk replaces the outer `Widget`, which removes its method,
28366    /// and then re-creates that method from the region. Creation alone would
28367    /// call the method recovered; it was there before the recovery opened, so
28368    /// the recovered set is empty and only the region's own reparse window is
28369    /// recorded (#2787).
28370    #[test]
28371    #[hardwon(
28372        "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
28373    )]
28374    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
28375        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
28376                      BEGIN_NS\n\
28377                      namespace demo { struct Widget { void doWork(); }; }\n\
28378                      END_NS\n";
28379        let parsed = parse_cpp_declarations(source, "restored.cpp");
28380        let recovered: Vec<String> = parsed
28381            .materialization_records
28382            .iter()
28383            .filter_map(|record| match record {
28384                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
28385                _ => None,
28386            })
28387            .collect();
28388        assert!(
28389            recovered.is_empty(),
28390            "the region declares nothing the file did not already declare: {recovered:?}"
28391        );
28392        let mut names: Vec<String> = parsed
28393            .declarations()
28394            .iter()
28395            .map(|unit| unit.fq_name())
28396            .collect();
28397        names.sort();
28398        assert_eq!(
28399            names,
28400            vec![
28401                "demo".to_string(),
28402                "demo.Widget".to_string(),
28403                "demo.Widget.doWork".to_string(),
28404            ]
28405        );
28406    }
28407
28408    /// Four macro-sentinel recoveries in one file (#941). Each one must record
28409    /// exactly the declarations it minted -- not the ones an earlier recovery
28410    /// minted, and not the file's other declarations -- in start-byte order,
28411    /// and each record must carry its own reparse window (#2787).
28412    #[test]
28413    #[hardwon(
28414        "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28415    )]
28416    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28417        let mut source = String::new();
28418        for index in 0..4 {
28419            let _ = write!(
28420                source,
28421                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
28422            );
28423        }
28424        source.push_str("void outside() {}\n");
28425        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28426
28427        let recovered: Vec<(String, (usize, usize))> = parsed
28428            .materialization_records
28429            .iter()
28430            .filter_map(|record| match record {
28431                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28432                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28433                }
28434                _ => None,
28435            })
28436            .collect();
28437
28438        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28439        for index in 0..4 {
28440            // The window the reparse covers: everything after the opening
28441            // sentinel token up to the newline before the closing one.
28442            let region = format!("namespace demo{index}");
28443            let region_start = source.find(&region).expect("each region is in the source");
28444            let start = source[..region_start]
28445                .rfind("BEGIN_NS")
28446                .expect("each region opens with a sentinel")
28447                + "BEGIN_NS".len();
28448            let end = start
28449                + source[start..]
28450                    .find("END_NS")
28451                    .expect("each region closes with a sentinel")
28452                - 1;
28453            let window = (start, end);
28454            for name in [
28455                format!("demo{index}"),
28456                format!("demo{index}.Widget{index}"),
28457                format!("demo{index}.Widget{index}.doWork{index}"),
28458            ] {
28459                expected.push((name, window));
28460            }
28461        }
28462        assert_eq!(
28463            recovered, expected,
28464            "each recovery records its own minted declarations, in order"
28465        );
28466        assert!(
28467            parsed
28468                .declarations()
28469                .iter()
28470                .any(|unit| unit.fq_name() == "outside"),
28471            "the declaration outside every region stays parsed and unrecovered"
28472        );
28473    }
28474
28475    /// `blocks` copies of one plain class-with-methods template. The class the
28476    /// question is about is always the first one, so the only thing that
28477    /// changes between two of these sources is how much unrelated tree
28478    /// surrounds it.
28479    fn repeated_class_blocks(blocks: usize) -> String {
28480        let mut source = String::from("namespace demo {\n");
28481        for index in 0..blocks {
28482            let _ = write!(
28483                source,
28484                "\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"
28485            );
28486        }
28487        source.push_str("\n}\n");
28488        source
28489    }
28490
28491    /// #1496: asking whether a recovered class shape claims one range must cost
28492    /// the path to that range, not a pass over the whole translation unit.
28493    ///
28494    /// The C++ inverse scan asks this once per candidate type reference, so a
28495    /// whole-tree walk makes one file's scan quadratic in its own size. Both
28496    /// sources here answer `None` -- these are plain classes that no recovery
28497    /// shape claims -- which is exactly the case that used to pay full price.
28498    #[test]
28499    #[hardwon(
28500        "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28501    )]
28502    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28503        let mut answers = Vec::new();
28504        let mut visits = Vec::new();
28505        let mut node_counts = Vec::new();
28506        for blocks in [200usize, 400] {
28507            let source = repeated_class_blocks(blocks);
28508            let mut parser = tree_sitter::Parser::new();
28509            parser
28510                .set_language(&tree_sitter_cpp::LANGUAGE.into())
28511                .unwrap();
28512            let tree = parser.parse(&source, None).unwrap();
28513            let start_byte = source.find("class Widget0 ").expect("first class");
28514            let end_byte = start_byte
28515                + source[start_byte..]
28516                    .find("};")
28517                    .expect("first class terminator")
28518                + "};".len();
28519            let range = Range {
28520                start_byte,
28521                end_byte,
28522                start_line: 0,
28523                end_line: 0,
28524            };
28525            reset_recovered_class_body_node_visits_for_test();
28526            let recovered_export_classes =
28527                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28528            answers.push(recovered_class_body_at(
28529                &recovered_export_classes,
28530                tree.root_node(),
28531                &source,
28532                "Widget0",
28533                &range,
28534            ));
28535            visits.push(recovered_class_body_node_visits_for_test());
28536            let mut nodes = 0usize;
28537            let mut stack = vec![tree.root_node()];
28538            while let Some(node) = stack.pop() {
28539                nodes += 1;
28540                let mut cursor = node.walk();
28541                stack.extend(node.named_children(&mut cursor));
28542            }
28543            node_counts.push(nodes);
28544        }
28545
28546        assert_eq!(
28547            answers,
28548            vec![None, None],
28549            "no recovered shape claims a plain class"
28550        );
28551        assert_eq!(
28552            visits[0], visits[1],
28553            "the walk must follow the range's own path, so doubling the unrelated \
28554             classes must not change the node count: {visits:?} over trees of \
28555             {node_counts:?} nodes"
28556        );
28557        assert!(
28558            visits[1] * 20 < node_counts[1],
28559            "the walk must stay far below one pass over the tree: {visits:?} over \
28560             trees of {node_counts:?} nodes"
28561        );
28562    }
28563
28564    #[test]
28565    #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
28566    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
28567        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
28568        let mut parser = tree_sitter::Parser::new();
28569        parser
28570            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28571            .expect("C++ grammar");
28572        let tree = parser.parse(source, None).expect("fixture tree");
28573        let mut stack = vec![tree.root_node()];
28574        let mut recovered = None;
28575        while let Some(node) = stack.pop() {
28576            if node.kind() == "field_declaration"
28577                && let Some(field) = recovered_function_like_field_declarator(node, source)
28578            {
28579                recovered = Some((node, field.name));
28580                break;
28581            }
28582            let mut cursor = node.walk();
28583            stack.extend(node.named_children(&mut cursor));
28584        }
28585        let (declaration, name) = recovered.unwrap_or_else(|| {
28586            panic!(
28587                "pointer-wrapped macro field was not recovered: {}",
28588                tree.root_node().to_sexp()
28589            )
28590        });
28591        assert_eq!(node_text(name, source), "handshake");
28592        let recovered =
28593            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
28594        assert_eq!(recovered.pointer_depth(), 1);
28595        assert_eq!(
28596            render_cpp_field_signature(declaration, name, source),
28597            "struct handshake * handshake;"
28598        );
28599    }
28600
28601    #[test]
28602    #[hardwon(
28603        "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
28604    )]
28605    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
28606        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
28607        let mut parser = tree_sitter::Parser::new();
28608        parser
28609            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28610            .expect("C++ grammar");
28611        let tree = parser.parse(source, None).expect("fixture tree");
28612        // The macro-declaration grammar reads `PyObject_HEAD` as a
28613        // `macro_modifier`, so the member arrives as an ordinary clean field
28614        // declaration rather than the recovery shape the ERROR-based reader
28615        // exists for.
28616        let mut stack = vec![tree.root_node()];
28617        let mut declaration = None;
28618        while let Some(node) = stack.pop() {
28619            if node.kind() == "field_declaration" {
28620                declaration = Some(node);
28621                break;
28622            }
28623            let mut cursor = node.walk();
28624            stack.extend(node.named_children(&mut cursor));
28625        }
28626        let declaration = declaration.unwrap_or_else(|| {
28627            panic!(
28628                "the pointer field must arrive as an ordinary field declaration: {}",
28629                tree.root_node().to_sexp()
28630            )
28631        });
28632        let declarator = declaration
28633            .child_by_field_name("declarator")
28634            .expect("pointer field declarator");
28635        assert_eq!(node_text(declarator, source), "*image");
28636        assert_eq!(
28637            render_cpp_field_signature(declaration, declarator, source),
28638            "PyObject_HEAD ImagingObject * image;",
28639            "the macro marker is retained as modifier provenance, the same \
28640             treatment an attribute or storage-class specifier gets"
28641        );
28642        assert_eq!(
28643            crate::source_facts::capture_field_type(declaration, "image", source),
28644            (
28645                Some(
28646                    brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
28647                        type_text: "ImagingObject".to_owned(),
28648                        indirection: 1,
28649                        binds_indirectly: true,
28650                        template_arguments: None,
28651                    }
28652                ),
28653                false,
28654            ),
28655            "canonical field facts must use the declared type, not the macro token"
28656        );
28657    }
28658
28659    #[test]
28660    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
28661        let source = "class outer { class inner { int v; }; };\n";
28662        let c_parsed = parse_cpp_declarations(source, "k.c");
28663        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
28664        let c_declarations = c_parsed.declarations();
28665        let cpp_declarations = cpp_parsed.declarations();
28666        assert!(
28667            c_declarations
28668                .iter()
28669                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28670            "{c_declarations:?}"
28671        );
28672        assert_eq!(
28673            c_declarations
28674                .iter()
28675                .map(|unit| unit.fq_name())
28676                .collect::<std::collections::BTreeSet<_>>(),
28677            cpp_declarations
28678                .iter()
28679                .map(|unit| unit.fq_name())
28680                .collect::<std::collections::BTreeSet<_>>()
28681        );
28682    }
28683    #[test]
28684    fn callable_metadata_records_c_receiver_contracts_structurally() {
28685        const SOURCE: &str = concat!(
28686            "static long accumulate(const long *lines, int count) { return count; }\n",
28687            "\n",
28688            "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
28689        );
28690        let parsed = parse_cpp_declarations(SOURCE, "shop.c");
28691
28692        assert_eq!(
28693            recorded_callable_modifiers(&parsed),
28694            vec![
28695                "accumulate decl_only=false static=false constructor=false \
28696                 visibility=Unknown"
28697                    .to_string(),
28698                "total decl_only=false static=false constructor=false visibility=Unknown"
28699                    .to_string(),
28700            ],
28701        );
28702    }
28703    #[test]
28704    fn callable_metadata_records_cpp_receiver_contracts_structurally() {
28705        const SOURCE: &str = concat!(
28706            "namespace shop {\n",
28707            "\n",
28708            "class Client {\n",
28709            "public:\n",
28710            "    Client();\n",
28711            "    void send(int order);\n",
28712            "    static int count();\n",
28713            "protected:\n",
28714            "    virtual int audit(int n) const { return n; }\n",
28715            "private:\n",
28716            "    int tally(int n) { return n; }\n",
28717            "};\n",
28718            "\n",
28719            "struct Ledger {\n",
28720            "    void record(int n) { (void)n; }\n",
28721            "};\n",
28722            "\n",
28723            "Client::Client() {}\n",
28724            "void Client::send(int order) { (void)order; }\n",
28725            "int Client::count() { return 0; }\n",
28726            "\n",
28727            "int total(int order) { return order; }\n",
28728            "\n",
28729            "}\n",
28730        );
28731        let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
28732
28733        assert_eq!(
28734            recorded_callable_modifiers(&parsed),
28735            vec![
28736                "shop.Client.Client decl_only=false static=false constructor=true \
28737                 visibility=Unknown"
28738                    .to_string(),
28739                "shop.Client.Client decl_only=true static=false constructor=true \
28740                 visibility=Public"
28741                    .to_string(),
28742                "shop.Client.audit decl_only=false static=false constructor=false \
28743                 visibility=Protected"
28744                    .to_string(),
28745                "shop.Client.count decl_only=false static=false constructor=false \
28746                 visibility=Unknown"
28747                    .to_string(),
28748                "shop.Client.count decl_only=true static=true constructor=false \
28749                 visibility=Public"
28750                    .to_string(),
28751                "shop.Client.send decl_only=false static=false constructor=false \
28752                 visibility=Unknown"
28753                    .to_string(),
28754                "shop.Client.send decl_only=true static=false constructor=false \
28755                 visibility=Public"
28756                    .to_string(),
28757                "shop.Client.tally decl_only=false static=false constructor=false \
28758                 visibility=Private"
28759                    .to_string(),
28760                "shop.Ledger.record decl_only=false static=false constructor=false \
28761                 visibility=Public"
28762                    .to_string(),
28763                "shop.total decl_only=false static=false constructor=false visibility=Unknown"
28764                    .to_string(),
28765            ],
28766        );
28767    }
28768
28769    #[test]
28770    fn reparsed_member_signature_grammar_contract() {
28771        use crate::grammar_test_support::{nodes, tree};
28772
28773        // These are class-member fragments reparsed at translation-unit scope,
28774        // exactly the boundary the recovery adapter receives from the grammar.
28775        // Identifier-only annotation errors retain a complete declaration;
28776        // literal payloads and missing signature tokens cannot be admitted.
28777        for (source, expected, error_children, missing_close) in [
28778            ("int f(int n) const { return n; }", true, vec![], false),
28779            (
28780                "int f(int n) const nonneg { return n; }",
28781                true,
28782                vec!["identifier"],
28783                false,
28784            ),
28785            (
28786                "int f(int n) ATTR 42 { return n; }",
28787                false,
28788                vec!["identifier", "number_literal"],
28789                false,
28790            ),
28791            (
28792                "int f(int n = 42 43) { return n; }",
28793                false,
28794                vec!["number_literal"],
28795                false,
28796            ),
28797            ("int f(int n { return n; }", false, vec![], true),
28798        ] {
28799            let parsed = tree(source);
28800            let root = parsed.root_node();
28801            let function = root.named_child(0).unwrap();
28802            assert_eq!(
28803                function.kind(),
28804                "function_definition",
28805                "{source}: {}",
28806                root.to_sexp()
28807            );
28808            let declarator = function.child_by_field_name("declarator").unwrap();
28809            assert_eq!(declarator.kind(), "function_declarator");
28810            let parameters = declarator.child_by_field_name("parameters").unwrap();
28811            assert_eq!(parameters.kind(), "parameter_list");
28812            assert_eq!(
28813                function.child_by_field_name("body").unwrap().kind(),
28814                "compound_statement"
28815            );
28816            let all_nodes = nodes(function);
28817            let errors: Vec<_> = all_nodes
28818                .iter()
28819                .filter(|node| node.kind() == "ERROR")
28820                .collect();
28821            let kinds: Vec<_> = errors
28822                .iter()
28823                .flat_map(|error| {
28824                    error
28825                        .named_children(&mut error.walk())
28826                        .map(|child| child.kind())
28827                        .collect::<Vec<_>>()
28828                })
28829                .collect();
28830            assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
28831            assert_eq!(
28832                all_nodes
28833                    .iter()
28834                    .any(|node| node.is_missing() && node.kind() == ")"),
28835                missing_close,
28836                "{source}"
28837            );
28838            assert_eq!(
28839                cpp_reparsed_members_are_indexable(root, source),
28840                expected,
28841                "{source}: {}",
28842                root.to_sexp()
28843            );
28844        }
28845    }
28846    #[test]
28847    fn qualified_and_template_declarator_name_grammar_contract() {
28848        use crate::grammar_test_support::{nodes, tree};
28849        let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
28850        let parsed = tree(source);
28851        let all = nodes(parsed.root_node());
28852        for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
28853            let qualified = all
28854                .iter()
28855                .copied()
28856                .find(|node| {
28857                    node.kind() == "qualified_identifier" && node_text(*node, source) == written
28858                })
28859                .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
28860            let scope = qualified.child_by_field_name("scope").unwrap();
28861            assert_eq!(node_text(scope, source), owner);
28862            assert!(qualified.child_by_field_name("name").is_some());
28863            assert_eq!(extract_declarator_name(qualified, source), written);
28864        }
28865        let function = all
28866            .iter()
28867            .copied()
28868            .find(|node| {
28869                node.kind() == "function_declarator"
28870                    && node
28871                        .child_by_field_name("declarator")
28872                        .is_some_and(|name| name.kind() == "template_function")
28873            })
28874            .unwrap_or_else(|| {
28875                panic!(
28876                    "missing template declarator: {}",
28877                    parsed.root_node().to_sexp()
28878                )
28879            });
28880        let template = function.child_by_field_name("declarator").unwrap();
28881        let name = template.child_by_field_name("name").unwrap();
28882        assert_eq!(name.kind(), "identifier");
28883        assert_eq!(node_text(name, source), "make");
28884        assert_eq!(
28885            template.child_by_field_name("arguments").unwrap().kind(),
28886            "template_argument_list"
28887        );
28888        assert_eq!(cpp_function_declarator_name_node(function), Some(name));
28889    }
28890
28891    fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
28892        let mut declarations = file
28893            .declarations()
28894            .iter()
28895            .map(|unit| {
28896                let callable = unit.is_function().then(|| {
28897                    let mut labels = file
28898                        .signature_metadata
28899                        .get(unit)
28900                        .into_iter()
28901                        .flatten()
28902                        .map(|metadata| {
28903                            metadata
28904                                .parameters()
28905                                .iter()
28906                                .map(|parameter| parameter.label())
28907                                .collect::<Vec<_>>()
28908                        })
28909                        .collect::<Vec<_>>();
28910                    labels.sort();
28911                    (unit.signature(), labels)
28912                });
28913                let fields = unit.is_field().then(|| {
28914                    let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
28915                    signatures.sort();
28916                    signatures.dedup();
28917                    signatures
28918                });
28919                format!(
28920                    "{:?} {} {fields:?} {callable:?}",
28921                    unit.kind(),
28922                    unit.fq_name()
28923                )
28924            })
28925            .collect::<Vec<_>>();
28926        declarations.sort();
28927        declarations
28928    }
28929
28930    /// Metamorphic oracle: erasing declaration decorators, expanding field-list
28931    /// macros, and spelling a namespace explicitly preserve declaration identity.
28932    /// Compare the complete inventory so a recovered initializer or macro token
28933    /// cannot pass merely because the intended callable was also recovered.
28934    #[test]
28935    fn recovery_declarations_agree_with_explicit_expansions() {
28936        let cases = [
28937            (
28938                "decorated constructors",
28939                "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",
28940                "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",
28941            ),
28942            (
28943                "namespace sentinel",
28944                "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
28945                "namespace demo { class Widget { public: int first; void method(); }; }\n",
28946            ),
28947            (
28948                "wrapped prototypes",
28949                "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",
28950                "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
28951            ),
28952            (
28953                "namespace forward across directives",
28954                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
28955                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
28956            ),
28957            (
28958                "local forward does not lend namespace",
28959                "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
28960                "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
28961            ),
28962            (
28963                "object declarator does not lend namespace",
28964                "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
28965                "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
28966            ),
28967            (
28968                "const template return",
28969                r###"template<class T> struct C { API const T f() { return {}; } };"###,
28970                r###"template<class T> struct C { const T f() { return {}; } };"###,
28971            ),
28972            (
28973                "collapsed struct and union",
28974                r###"#define FIRST int first;
28975#define SECOND int first; int second;
28976union One { FIRST SECOND
28977};
28978struct Two { FIRST SECOND
28979};
28980struct Tail { UNDEFINED
28981};
28982"###,
28983                r###"#define FIRST int first;
28984#define SECOND int first; int second;
28985union One { int first; int first; int second;
28986};
28987struct Two { int first; int first; int second;
28988};
28989struct Tail { UNDEFINED
28990};
28991"###,
28992            ),
28993            (
28994                "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
28995                r###"
28996#pragma once
28997#define DEMO_DEPRECATED(message)
28998namespace demo {
28999struct Base {
29000    static int aligned(int value) { return value; }
29001    int legacy(int value) const
29002        DEMO_DEPRECATED("use replacement()") { return value; }
29003    int replacement() const;
29004    void run(int value);
29005};
29006struct OtherBase {
29007    void run(int value);
29008    static int aligned(int value) { return value; }
29009};
29010struct Derived : Base {};
29011struct Override : Base {
29012    void run(int value);
29013    static int aligned(int value) { return value; }
29014};
29015struct RecoveredOverride : Base {
29016    int legacy(int value) const
29017        DEMO_DEPRECATED("use replacement()") { return value; }
29018    void run(int value);
29019};
29020struct Hidden : Base {
29021    void run(int first, int second);
29022    static int aligned(int first, int second) { return first + second; }
29023};
29024struct Ambiguous : Base, OtherBase {};
29025}
29026struct Global {};
29027"###,
29028                r###"
29029#pragma once
29030#define DEMO_DEPRECATED(message)
29031namespace demo {
29032struct Base {
29033    static int aligned(int value) { return value; }
29034    int legacy(int value) const
29035         { return value; }
29036    int replacement() const;
29037    void run(int value);
29038};
29039struct OtherBase {
29040    void run(int value);
29041    static int aligned(int value) { return value; }
29042};
29043struct Derived : Base {};
29044struct Override : Base {
29045    void run(int value);
29046    static int aligned(int value) { return value; }
29047};
29048struct RecoveredOverride : Base {
29049    int legacy(int value) const
29050         { return value; }
29051    void run(int value);
29052};
29053struct Hidden : Base {
29054    void run(int first, int second);
29055    static int aligned(int first, int second) { return first + second; }
29056};
29057struct Ambiguous : Base, OtherBase {};
29058}
29059struct Global {};
29060"###,
29061            ),
29062            (
29063                "sentinel union",
29064                r###"BEGIN_NS
29065union Storage {int value; char byte;};
29066END_NS
29067"###,
29068                r###"union Storage {int value; char byte;};
29069"###,
29070            ),
29071            (
29072                "compact sentinel class",
29073                r###"BEGIN_NS
29074class Widget {int first; void method();};
29075END_NS
29076"###,
29077                r###"class Widget {int first; void method();};
29078"###,
29079            ),
29080            (
29081                "three decorators",
29082                "class C { FIRST SECOND THIRD void f() {} };",
29083                "class C { void f() {} };",
29084            ),
29085            (
29086                "overlapping field lists",
29087                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
29088                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
29089            ),
29090            (
29091                "identical redefinition",
29092                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
29093                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
29094            ),
29095            (
29096                "unconditional reset",
29097                "#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",
29098                "#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",
29099            ),
29100        ];
29101        for (label, source_lf, expanded_lf) in cases {
29102            for (source, expanded) in [
29103                (source_lf.to_owned(), expanded_lf.to_owned()),
29104                (
29105                    source_lf.replace('\n', "\r\n"),
29106                    expanded_lf.replace('\n', "\r\n"),
29107                ),
29108            ] {
29109                let tree = crate::grammar_test_support::tree(&source);
29110                let nodes = crate::grammar_test_support::nodes(tree.root_node());
29111                match label {
29112                    "decorated constructors" => assert!(
29113                        nodes.iter().any(|node| {
29114                            node.kind() == "ERROR"
29115                                && node
29116                                    .named_child(0)
29117                                    .is_some_and(|head| head.kind() == "macro_modifier_declarator")
29118                        }),
29119                        "{}",
29120                        tree.root_node().to_sexp()
29121                    ),
29122                    "wrapped prototypes" => assert!(
29123                        tree.root_node().named_child(0).is_some_and(|node| {
29124                            node.kind() == "ERROR"
29125                                && node
29126                                    .named_child(0)
29127                                    .is_some_and(|head| head.kind() == "function_declarator")
29128                        }),
29129                        "{}",
29130                        tree.root_node().to_sexp()
29131                    ),
29132                    "collapsed struct and union" => assert!(
29133                        nodes.iter().any(|node| {
29134                            node.kind() == "union"
29135                                && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
29136                        }),
29137                        "{}",
29138                        tree.root_node().to_sexp()
29139                    ),
29140                    "object declarator does not lend namespace" => assert!(
29141                        nodes
29142                            .iter()
29143                            .any(|node| { node.kind() == "parenthesized_declarator" }),
29144                        "{}",
29145                        tree.root_node().to_sexp()
29146                    ),
29147                    _ => {}
29148                }
29149                let parsed = parse_cpp_declarations(&source, "expansion.h");
29150                let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29151                assert_eq!(
29152                    declaration_expansion_inventory(&parsed),
29153                    declaration_expansion_inventory(&explicit),
29154                    "{label}"
29155                );
29156                for unit in parsed.declarations() {
29157                    for range in parsed.declaration_ranges(unit) {
29158                        assert!(
29159                            range.start_byte < range.end_byte,
29160                            "{label}: {unit:?}: {range:?}"
29161                        );
29162                        for (offset, line) in [
29163                            (range.start_byte, range.start_line),
29164                            (range.end_byte, range.end_line),
29165                        ] {
29166                            assert_eq!(
29167                                line,
29168                                source[..offset]
29169                                    .bytes()
29170                                    .filter(|byte| *byte == b'\n')
29171                                    .count()
29172                                    + 1,
29173                                "{label}: {unit:?}: {range:?}"
29174                            );
29175                        }
29176                    }
29177                }
29178            }
29179        }
29180    }
29181
29182    #[test]
29183    #[hardwon(
29184        "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
29185    )]
29186    fn recovered_class_constructor_agrees_with_explicit_expansion() {
29187        let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
29188        let tree = crate::grammar_test_support::tree(source);
29189        crate::grammar_test_support::assert_function_like_export_class_head(&tree, source, "value");
29190        let actual = parse_cpp_declarations(source, "constructor.h");
29191        let explicit = parse_cpp_declarations(
29192            "class value { public: value(pn_data_t* d); int following(); };",
29193            "constructor.h",
29194        );
29195        assert_eq!(function_identities(&actual), function_identities(&explicit));
29196    }
29197
29198    #[test]
29199    #[hardwon(
29200        "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
29201    )]
29202    fn recovered_base_initializer_is_not_a_declaration() {
29203        let source = r###"
29204namespace api {
29205
29206/**
29207* Doc comment
29208*/
29209class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
29210   public:
29211      /**
29212      * Construct from a point.
29213      */
29214      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
29215
29216#if defined(PROJECT_HAS_LEGACY_POINT)
29217      /**
29218      * Construct from a legacy point.
29219      */
29220      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
29221#endif
29222
29223      std::string algo_name() const override;
29224
29225      AlgorithmIdentifier algorithm_identifier() const override;
29226};
29227
29228}
29229"###;
29230        let tree = crate::grammar_test_support::tree(source);
29231        crate::grammar_test_support::assert_function_like_export_class_head(
29232            &tree,
29233            source,
29234            "VirtualBased",
29235        );
29236        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29237        let actual = parse_cpp_declarations(source, "virtual-base.h");
29238        let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
29239        assert_eq!(
29240            declaration_expansion_inventory(&actual),
29241            declaration_expansion_inventory(&explicit)
29242        );
29243    }
29244
29245    #[test]
29246    #[hardwon("#6902: a semicolon-free macro envelope hid the records and restore after it")]
29247    fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
29248        let source = r###"struct Aggregate;
29249
29250#define NOARG
29251LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
29252#undef NOARG
29253
29254struct Context { int value; };
29255struct Visit { int value; };
29256
29257LIST_DECLS(static, deps, struct Aggregate *, unused)
29258LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
29259
29260static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29261
29262static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
29263static struct Context *restore(struct Aggregate *value) {
29264  return (struct Context *) value;
29265}
29266
29267static void caller(struct Context *ctx, struct Visit *visit) {
29268  target(ctx, 0, 0, visit); /* recovered prototype */
29269}
29270
29271static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29272  (void) ctx;
29273  (void) first;
29274  (void) second;
29275  (void) visit;
29276}
29277"###;
29278        let expanded = r###"struct Aggregate;
29279
29280#define NOARG
29281#undef NOARG
29282
29283struct Context { int value; };
29284struct Visit { int value; };
29285
29286
29287static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29288
29289static struct Context *restore(struct Aggregate *value);
29290static struct Context *restore(struct Aggregate *value) {
29291  return (struct Context *) value;
29292}
29293
29294static void caller(struct Context *ctx, struct Visit *visit) {
29295  target(ctx, 0, 0, visit); /* recovered prototype */
29296}
29297
29298static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29299  (void) ctx;
29300  (void) first;
29301  (void) second;
29302  (void) visit;
29303}
29304"###;
29305        let tree = crate::grammar_test_support::tree(source);
29306        assert!(
29307            tree.root_node().has_error(),
29308            "{}",
29309            tree.root_node().to_sexp()
29310        );
29311        let actual = parse_cpp_declarations(source, "expansion.h");
29312        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29313        assert_eq!(
29314            declaration_expansion_inventory(&actual),
29315            declaration_expansion_inventory(&explicit)
29316        );
29317    }
29318
29319    #[test]
29320    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29321    fn recovered_sentinel_alias_agrees_with_expansion() {
29322        let source = r###"namespace absl {
29323ABSL_NAMESPACE_BEGIN namespace container_internal {
29324
29325class raw_hash_set : public Base {
29326 public:
29327  using value_type = int;
29328
29329  template <class U,
29330            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29331  void insert(U value) { (void)value; }
29332
29333  struct InsertSlot {
29334    raw_hash_set& s;
29335  };
29336};
29337
29338}
29339ABSL_NAMESPACE_END
29340}"###;
29341        let expanded = r###"namespace absl {
29342namespace container_internal {
29343
29344class raw_hash_set : public Base {
29345 public:
29346  using value_type = int;
29347
29348  template <class U>
29349  void insert(U value) { (void)value; }
29350
29351  struct InsertSlot {
29352    raw_hash_set& s;
29353  };
29354};
29355
29356}
29357
29358}"###;
29359        let tree = crate::grammar_test_support::tree(source);
29360        assert!(
29361            tree.root_node().has_error(),
29362            "{}",
29363            tree.root_node().to_sexp()
29364        );
29365        let actual = parse_cpp_declarations(source, "expansion.h");
29366        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29367        assert_eq!(
29368            declaration_expansion_inventory(&actual),
29369            declaration_expansion_inventory(&explicit)
29370        );
29371    }
29372
29373    /// The structured alias facts the resolver and navigation read, for one
29374    /// declaration of `fq_name`.
29375    fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
29376        let units = file
29377            .declarations()
29378            .iter()
29379            .filter(|unit| unit.fq_name() == fq_name)
29380            .collect::<Vec<_>>();
29381        let [unit] = units.as_slice() else {
29382            panic!("expected one {fq_name}: {:#?}", file.declarations());
29383        };
29384        let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
29385        let fact = file
29386            .source_declaration_units
29387            .iter()
29388            .filter(|(_, linked)| linked == *unit)
29389            .map(|(declaration, _)| {
29390                facts
29391                    .declarations
29392                    .iter()
29393                    .find(|fact| fact.declaration == *declaration)
29394                    .expect("alias source fact")
29395            })
29396            .map(|fact| {
29397                (
29398                    &fact.alias_target,
29399                    &fact.alias_target_text,
29400                    &fact.lexical_path,
29401                    &fact.guard_requirements,
29402                    &fact.file_scope_alias,
29403                )
29404            })
29405            .collect::<Vec<_>>();
29406        let parents = file
29407            .children
29408            .iter()
29409            .filter(|(_, children)| children.contains(unit))
29410            .map(|(parent, _)| parent.fq_name())
29411            .collect::<Vec<_>>();
29412        format!(
29413            "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29414            unit.kind(),
29415            file.type_aliases.contains(unit),
29416            file.signatures.get(unit),
29417            file.signature_metadata.get(unit),
29418            file.cpp_template_metadata.get(unit),
29419        )
29420    }
29421
29422    #[test]
29423    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29424    fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29425        let source = r###"namespace absl {
29426ABSL_NAMESPACE_BEGIN namespace container_internal {
29427
29428class raw_hash_set : public Base {
29429 public:
29430  using value_type = int;
29431  template <class T>
29432  using rebind = Base<T>;
29433  using key_type = value_type;
29434#ifdef ABSL_HAVE_SLOT
29435  using slot_type = long;
29436#endif
29437
29438  template <class U,
29439            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29440  void insert(U value) { (void)value; }
29441
29442  struct InsertSlot {
29443    raw_hash_set& s;
29444  };
29445};
29446
29447}
29448ABSL_NAMESPACE_END
29449}"###;
29450        let expanded = source
29451            .replace("ABSL_NAMESPACE_BEGIN ", "")
29452            .replace("ABSL_NAMESPACE_END", "")
29453            .replace(
29454                ",\n            REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29455                ">",
29456            );
29457        assert!(
29458            crate::grammar_test_support::tree(source)
29459                .root_node()
29460                .has_error()
29461        );
29462        assert!(
29463            !crate::grammar_test_support::tree(&expanded)
29464                .root_node()
29465                .has_error()
29466        );
29467        let actual = parse_cpp_declarations(source, "expansion.h");
29468        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29469        assert_eq!(
29470            declaration_expansion_inventory(&actual),
29471            declaration_expansion_inventory(&explicit)
29472        );
29473        for (alias, text) in [
29474            ("value_type", "using value_type = int;"),
29475            ("rebind", "using rebind = Base<T>;"),
29476            ("key_type", "using key_type = value_type;"),
29477            ("slot_type", "using slot_type = long;"),
29478        ] {
29479            let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29480            // Kind, owner, target, template head, guard, and lexical scope all
29481            // agree with the explicitly written class.
29482            assert_eq!(
29483                sentinel_alias_facts(&actual, &fq_name),
29484                sentinel_alias_facts(&explicit, &fq_name),
29485                "{alias}"
29486            );
29487            // The range is the alias's own source text, line included.
29488            let unit = actual
29489                .declarations()
29490                .iter()
29491                .find(|unit| unit.fq_name() == fq_name)
29492                .unwrap();
29493            let start = source.find(text).unwrap();
29494            assert_eq!(
29495                actual.declaration_ranges(unit),
29496                [Range {
29497                    start_byte: start,
29498                    end_byte: start + text.len(),
29499                    start_line: source[..start].matches('\n').count() + 1,
29500                    end_line: source[..start].matches('\n').count() + 1,
29501                }],
29502                "{alias}"
29503            );
29504        }
29505        let rendered =
29506            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29507        assert!(
29508            rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29509                && rendered
29510                    .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29511            "the target resolves in the recovered class: {rendered}"
29512        );
29513        assert!(
29514            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29515                .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29516            "the guarded alias keeps its guard"
29517        );
29518    }
29519
29520    #[test]
29521    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29522    fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29523        // Without the class's displaced `};` the sentinel recovery cannot
29524        // prove where raw_hash_set ends, so it recovers no class at all.
29525        let source = r###"namespace absl {
29526ABSL_NAMESPACE_BEGIN namespace container_internal {
29527
29528class raw_hash_set : public Base {
29529 public:
29530  using value_type = int;
29531
29532  template <class U,
29533            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29534  void insert(U value) { (void)value; }
29535
29536}
29537ABSL_NAMESPACE_END
29538}"###;
29539        let parsed = parse_cpp_declarations(source, "unproven.h");
29540        let declarations = parsed.declarations();
29541        assert!(
29542            declarations
29543                .iter()
29544                .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29545            "no member may claim an owner the recovery did not prove: {declarations:#?}"
29546        );
29547        assert!(
29548            declarations
29549                .iter()
29550                .filter(|unit| unit.identifier() == "value_type")
29551                .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29552            "the alias must not become a field: {declarations:#?}"
29553        );
29554    }
29555
29556    #[test]
29557    #[hardwon("#6902: consecutive decorated export classes lost most of their members")]
29558    fn recovered_consecutive_export_classes_agree_with_expansion() {
29559        let source =
29560            function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
29561        let tree = crate::grammar_test_support::tree(&source);
29562        for name in ["Exception", "Invalid_Argument"] {
29563            crate::grammar_test_support::assert_function_like_export_class_head(
29564                &tree, &source, name,
29565            );
29566        }
29567        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29568        let actual = parse_cpp_declarations(&source, "expansion.h");
29569        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29570        assert_eq!(
29571            declaration_expansion_inventory(&actual),
29572            declaration_expansion_inventory(&explicit)
29573        );
29574    }
29575
29576    #[test]
29577    #[hardwon(
29578        "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
29579    )]
29580    fn standalone_field_list_agrees_with_expansion() {
29581        let source =
29582            "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
29583        let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
29584        let tree = crate::grammar_test_support::tree(source);
29585        assert!(
29586            crate::grammar_test_support::nodes(tree.root_node())
29587                .iter()
29588                .any(|node| node.kind() == "ERROR"
29589                    && node
29590                        .named_child(0)
29591                        .is_some_and(|child| child.kind() == "identifier")),
29592            "{}",
29593            tree.root_node().to_sexp()
29594        );
29595        let actual = parse_cpp_declarations(source, "expansion.h");
29596        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29597        assert_eq!(
29598            declaration_expansion_inventory(&actual),
29599            declaration_expansion_inventory(&explicit)
29600        );
29601    }
29602
29603    #[test]
29604    #[hardwon(
29605        "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
29606    )]
29607    fn recovered_constructor_parameters_are_not_fields() {
29608        let source = r#"namespace Botan {
29609class EXPORT(2,0) Key final {
29610public:
29611    Key(int value);
29612    void run();
29613};
29614class EXPORT(2,0) DL_Group final {
29615public:
29616    ATTRIBUTE("use factory") explicit DL_Group(const char* name);
29617    DL_Group(int value);
29618    int get_p() const;
29619};
29620}"#;
29621        let actual = parse_cpp_declarations(source, "constructors.h");
29622        let expanded = source
29623            .replace("EXPORT(2,0) ", "")
29624            .replace("ATTRIBUTE(\"use factory\") ", "");
29625        let explicit = parse_cpp_declarations(&expanded, "constructors.h");
29626        assert_eq!(
29627            declaration_expansion_inventory(&actual),
29628            declaration_expansion_inventory(&explicit)
29629        );
29630    }
29631}