Skip to main content

brokk_bifrost_cpp/
declarations.rs

1//! The C++ declaration walk, including the macro-sentinel error recovery.
2//!
3//! Every function here is a pure function of a parsed tree and its source text.
4//! `analyzer/cpp/adapter.rs` in `brokk-bifrost-analysis` drives
5//! [`CppVisitor`] out of `LanguageAdapter::parse_file`.
6
7use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use crate::structural::{CPP_STRUCTURAL_SPEC, is_cpp_preprocessor_conditional};
10use crate::template_heads::cpp_out_of_line_owner_names;
11pub use brokk_bifrost_core::analyzer::common::node_source_text as node_text;
12use brokk_bifrost_core::analyzer::common::{
13    parse_source_ranges_with_cancellation, parse_source_region,
14};
15pub use brokk_bifrost_core::analyzer::cpp_facts::{
16    CppAccessSpecifier, CppBaseSpecifierFact, CppComparableNode, CppComparableParameter,
17    CppComparableSlot,
18};
19use brokk_bifrost_core::analyzer::fq_name::{
20    FqName, SegmentKind, joined_segments, normalize_joined, segment_interner,
21};
22use brokk_bifrost_core::analyzer::model::LanguageDialect;
23use brokk_bifrost_core::analyzer::model::{
24    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
25    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
26    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
27    ParameterMetadata, ParseCompleteness, Range, SignatureMetadata, StructuredTypeIdentity,
28    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
29};
30use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
31use brokk_bifrost_core::analyzer::source_facts::{
32    PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
33    SourceOccurrenceProvenance,
34};
35use brokk_bifrost_core::analyzer::statement_facts::StatementFactRows;
36use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
37use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
38use brokk_bifrost_core::analyzer::structural::materialization::{
39    GenerationKind, MaterializationRecord,
40};
41use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
42use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
43use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
44use brokk_bifrost_core::analyzer::tree_walk::{
45    NodeKindIds, ParentIndex, WalkControl, child_by_field_or_named_child_of_kinds, children_iter,
46    named_children_iter, push_children_reversed, push_named_children_reversed, subtree_contains,
47    walk_named_tree_preorder,
48};
49use brokk_bifrost_core::analyzer::usages::common::same_node;
50use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
51use brokk_bifrost_core::hash::{HashMap, HashSet};
52use regex::Regex;
53use std::cell::RefCell;
54use std::sync::Arc;
55use tree_sitter::{Node, Tree};
56
57/// Strip a tag keyword from the type spelling returned by C++ tooling.
58pub(crate) fn strip_tag_type_prefix(value: &str) -> &str {
59    let value = value.trim_start_matches("const ");
60    value
61        .strip_prefix("struct ")
62        .or_else(|| value.strip_prefix("class "))
63        .or_else(|| value.strip_prefix("enum "))
64        .unwrap_or(value)
65        .trim()
66}
67
68/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
69/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
70/// `::` head is exactly the mixed-separator store issue #1163 is about; the
71/// structured form records each namespace component, and the equivalence check
72/// renders it natively (with `::` between adjacent Package segments) so it
73/// round-trips to the legacy string. Splitting the already-joined string here is
74/// the M1 bridge — the legacy strings stay authoritative until M3.
75fn cpp_push_package(fq: &mut FqName, package_name: &str) {
76    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
77        fq.push(segment_interner().intern(component, SegmentKind::Package));
78    }
79}
80
81/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
82const CPP_PACKAGE_SEPARATOR: &str = "::";
83
84/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
85/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
86/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
87/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
88/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
89/// cpp-specific native rendering rule is needed for this chain.
90fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
91    let mut first = true;
92    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
93    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
94    // entry point, not re-inference of an already-structured name.
95    for component in chain.split('$').filter(|c| !c.is_empty()) {
96        let kind = if first {
97            SegmentKind::Type
98        } else {
99            SegmentKind::Nested
100        };
101        fq.push(segment_interner().intern(component, kind));
102        first = false;
103    }
104}
105
106/// Structured name for a C++ namespace module: every `::`-separated component is
107/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
108/// `short_name` with an empty `package_name`).
109fn cpp_namespace_fq(full_name: &str) -> FqName {
110    let mut fq = FqName::new();
111    cpp_push_package(&mut fq, full_name);
112    fq
113}
114
115/// The per-level namespace components a `namespace_definition`'s `name` field
116/// declares.
117///
118/// A C++17 nested definition (`namespace a::b::c`) parses as a
119/// `nested_namespace_specifier` whose named children are the per-level
120/// `namespace_identifier`s plus, for three or more levels, a further
121/// `nested_namespace_specifier`; the `::` separators, the optional per-level
122/// `inline`, and the leading global `::` are all anonymous tokens the walk
123/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
124/// segment path as the expanded `namespace a { namespace b { } }` form.
125///
126/// A shape outside that grammar is the deliberately ill-formed source the
127/// diagnostic corpora carry. Those keep their historical single-component
128/// reading of the raw name text, which the caller still joins to the lexical
129/// namespace exactly as before.
130fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
131    let mut components = Vec::new();
132    let mut stack = vec![node];
133    while let Some(current) = stack.pop() {
134        match current.kind() {
135            "namespace_identifier" | "identifier" => {
136                components.push(normalize_cpp_whitespace(node_text(current, source)));
137            }
138            "nested_namespace_specifier" => {
139                for index in (0..current.named_child_count()).rev() {
140                    stack.push(
141                        current
142                            .named_child(index)
143                            .expect("index below the node's own named child count"),
144                    );
145                }
146            }
147            _ => return cpp_raw_namespace_name_components(node, source),
148        }
149    }
150    if components.iter().any(String::is_empty) {
151        return cpp_raw_namespace_name_components(node, source);
152    }
153    components
154}
155
156/// [`cpp_namespace_name_components`] with each level's own name token range,
157/// for the one-Module-per-level declarations of the `namespace a::b`
158/// shorthand: the name a level records must come from the token that spells
159/// it (#4292), not from whichever construct the walk reached first.
160fn cpp_namespace_name_component_ranges(node: Node<'_>, source: &str) -> Vec<(String, Range)> {
161    let mut components = Vec::new();
162    let mut stack = vec![node];
163    while let Some(current) = stack.pop() {
164        match current.kind() {
165            "namespace_identifier" | "identifier" => {
166                components.push((
167                    normalize_cpp_whitespace(node_text(current, source)),
168                    cpp_declaration_range(current),
169                ));
170            }
171            "nested_namespace_specifier" => {
172                for index in (0..current.named_child_count()).rev() {
173                    stack.push(
174                        current
175                            .named_child(index)
176                            .expect("index below the node's own named child count"),
177                    );
178                }
179            }
180            _ => return Vec::new(),
181        }
182    }
183    if components.iter().any(|(name, _)| name.is_empty()) {
184        return Vec::new();
185    }
186    components
187}
188
189/// The historical reading of a namespace name node: its whole source text as
190/// one component, with a leading global `::` marker dropped so the caller's
191/// global-scope handling stays the AST boundary rather than a text prefix.
192///
193/// This is the recovery path for source outside the C++ grammar, so the text it
194/// returns can carry a separator that names nothing. react-native-windows
195/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
196/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
197/// trailing separator with no tail. The caller stores this component in
198/// `short_name` and derives the fq by splitting it back apart, so an empty tail
199/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
200/// here, at the one place the malformed text enters, rather than leaving each
201/// consumer to guard (#2353, a variant of #1878).
202fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
203    let start = node
204        .child(0)
205        .filter(|child| !child.is_named() && child.kind() == "::")
206        .map_or(node.start_byte(), |marker| marker.end_byte());
207    let text = normalize_cpp_whitespace(
208        source
209            .get(start..node.end_byte())
210            .expect("namespace name node covers one source range"),
211    );
212    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
213    if text.is_empty() {
214        return Vec::new();
215    }
216    vec![text]
217}
218
219/// The namespace definition ancestors of `node`, ordered from outermost to innermost.
220pub fn enclosing_cpp_namespace_nodes<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
221    let mut namespaces = Vec::new();
222    let mut current = node.parent();
223    while let Some(parent) = current {
224        if parent.kind() == "namespace_definition" {
225            namespaces.push(parent);
226        }
227        current = parent.parent();
228    }
229    namespaces.reverse();
230    namespaces
231}
232
233/// Return the named namespace path that structurally encloses `node`.
234///
235/// This intentionally follows namespace AST ancestors rather than inspecting
236/// source text. Anonymous namespaces are not representable in the legacy C++
237/// package field, so a path containing one fails closed.
238fn cpp_lexical_namespace_name<'tree>(node: Node<'tree>, source: &str) -> Option<String> {
239    let mut components = Vec::new();
240    for current in enclosing_cpp_namespace_nodes(node) {
241        let name_node = current.child_by_field_name("name")?;
242        let name = normalize_cpp_whitespace(node_text(name_node, source));
243        if name.is_empty() {
244            return None;
245        }
246        components.push(name);
247    }
248    if components.is_empty() {
249        return None;
250    }
251    // Same shared empty-component decision as `cpp_push_package`, which splits
252    // this string back apart: an enclosing namespace whose own name node is
253    // malformed contributes a component carrying its own separator (#2353).
254    Some(
255        normalize_joined(
256            &components.join(CPP_PACKAGE_SEPARATOR),
257            CPP_PACKAGE_SEPARATOR,
258        )
259        .into_owned(),
260    )
261}
262
263/// Nested-class `$` join for short names. An anonymous parent class (empty
264/// short_name) contributes no segment: the FqName bridge drops empty
265/// components, so a bare `parent$child` join would desync `short_name` from
266/// the fq and trip the package/short boundary assert in
267/// `CodeUnit::with_signature_and_fq` (#2140).
268fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
269    cpp_join_short_name(parent_short, name, '$')
270}
271
272/// Member `.` join for short names; same anonymous-parent guard as
273/// [`cpp_join_nested_short`] (#2140).
274fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
275    cpp_join_short_name(parent_short, name, '.')
276}
277
278fn cpp_join_short_name(parent_short: &str, name: &str, separator: char) -> String {
279    if parent_short.is_empty() {
280        name.to_string()
281    } else {
282        format!("{parent_short}{separator}{name}")
283    }
284}
285
286/// Structural fq for a leaf declaration: the parent unit's fq plus this
287/// declaration's own name as one segment (or the package segments plus the
288/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
289/// chain, so a literal `$` inside a source identifier (Cython template
290/// substitution points, gcc `$`-identifiers) survives instead of corrupting
291/// the chain and tripping the package/short boundary assert (#2140).
292fn cpp_leaf_fq(
293    package_name: &str,
294    parent: Option<&CodeUnit>,
295    name: &str,
296    kind_if_nested: SegmentKind,
297    kind_if_top: SegmentKind,
298) -> FqName {
299    if let Some(parent) = parent {
300        parent
301            .fq()
302            .clone()
303            .with_pushed(segment_interner().intern(name, kind_if_nested))
304    } else {
305        let mut fq = FqName::new();
306        cpp_push_package(&mut fq, package_name);
307        fq.push(segment_interner().intern(name, kind_if_top));
308        fq
309    }
310}
311
312/// Structured name for a member unit (function, field, enumerator). The
313/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
314/// the member has an owner, by `.member`; free functions and globals have no
315/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
316/// C++ member names never contain a literal `.`, so the single `.` (if any)
317/// separates the owner chain from the member.
318pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
319    let mut fq = FqName::new();
320    cpp_push_package(&mut fq, package_name);
321    match short_name.rsplit_once('.') {
322        Some((owner_chain, member)) => {
323            cpp_push_type_chain(&mut fq, owner_chain);
324            fq.push(segment_interner().intern(member, SegmentKind::Member));
325        }
326        None => fq.push(segment_interner().intern(short_name, SegmentKind::Member)),
327    }
328    fq
329}
330
331/// Structured name for a rendered C++ qualified name supplied at the input
332/// edge, such as an `Analyzer::definitions("ns.Outer$Inner")` argument.
333///
334/// The shared client-path splitter only knows `::`, `.`, `\`, `/` and `+`, so
335/// it hands back one component for a nested-class chain (`Outer$Inner`). An
336/// exact-name lookup then asks the store for owner `ns` with identifier
337/// `Outer$Inner`, while the declaration persists as a
338/// [`SegmentKind::Type`]/[`SegmentKind::Nested`] pair whose identifier is the
339/// class's own name (`Inner`). Rebuilding the `$` split from the same rule that
340/// emits it ([`cpp_push_type_chain`]) keeps the query's owner/terminal
341/// boundary identical to the persisted one. A `$` that is *not* a nesting join
342/// (gcc `$`-identifiers, #2140) cannot be told apart from this spelling here,
343/// and stays reachable through the identifier fallback, which seeks the whole
344/// component text as one spelling.
345pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
346    let mut fq = FqName::new();
347    for component in parse_symbol_path(Language::Cpp, rendered) {
348        // fqname-M4: sanctioned input-edge interning bridge: this BUILDS the FqName's
349        // Type/Nested split from the legacy `$`-joined nested-class chain, at the same
350        // construction boundary `cpp_push_type_chain` owns for extraction.
351        let mut first = true;
352        for part in component.split('$').filter(|part| !part.is_empty()) {
353            let kind = if first {
354                // A client-typed head claims no kind, exactly as
355                // `parse_symbol_path_fq` records it.
356                SegmentKind::Unknown
357            } else {
358                SegmentKind::Nested
359            };
360            fq.push(segment_interner().intern(part, kind));
361            first = false;
362        }
363    }
364    fq
365}
366
367/// Fully-kinded identities a C++ source spelling joined only by `::` can name.
368///
369/// [`parse_symbol_path`] records every component as [`SegmentKind::Unknown`],
370/// and `Unknown` re-renders with `.`. `Top::Only` therefore compares as
371/// `Top.Only`, and the exact-name retain drops the persisted nested identity
372/// `Top$Only` (`Type` + `Nested`). These interpretations are the kind
373/// placements extraction stores, so each exact query's parent and terminal
374/// match a persisted row and the structural retain keeps it.
375///
376/// A spelling that already uses `.` or `$` is a canonical rendering (#3520)
377/// and stays on [`cpp_rendered_name_fq`] alone. `base::android.ScopedJavaGlobalRef`
378/// is one of those: the `::` there is the package join the canonical name
379/// already spells.
380pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
381    if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
382        return Vec::new();
383    }
384    let texts = parse_symbol_path(Language::Cpp, rendered);
385    if texts.len() < 2 {
386        return Vec::new();
387    }
388    cpp_qualified_kind_interpretations(&texts)
389}
390
391/// Every namespace / type / member boundary a `::`-joined source spelling can
392/// persist as. `packages` is the number of leading [`SegmentKind::Package`]
393/// segments.
394fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
395    let len = texts.len();
396    let mut names = Vec::new();
397    names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
398    for packages in 0..len {
399        let rest = len - packages;
400        let mut type_chain = Vec::with_capacity(len);
401        type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
402        type_chain.push(SegmentKind::Type);
403        type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
404        names.push(cpp_fq_with_kinds(texts, type_chain));
405        if rest >= 2 {
406            let mut member = Vec::with_capacity(len);
407            member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
408            member.push(SegmentKind::Type);
409            member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
410            member.push(SegmentKind::Member);
411            names.push(cpp_fq_with_kinds(texts, member));
412        } else if packages >= 1 {
413            let mut function = vec![SegmentKind::Package; packages];
414            function.push(SegmentKind::Member);
415            names.push(cpp_fq_with_kinds(texts, function));
416        }
417    }
418    names
419}
420
421fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
422    assert_eq!(
423        texts.len(),
424        kinds.len(),
425        "a qualified interpretation names every segment"
426    );
427    let mut fq = FqName::new();
428    for (text, kind) in texts.iter().zip(kinds) {
429        fq.push(segment_interner().intern(text, kind));
430    }
431    fq
432}
433
434#[derive(Clone)]
435pub struct ScopeInfo {
436    package_name: String,
437    module: Option<CodeUnit>,
438    class_unit: Option<CodeUnit>,
439    template_signature: Option<String>,
440    template_metadata: Option<CppTemplateMetadata>,
441    declarations_are_fields: bool,
442    recovered_specialization_member_scope: bool,
443    /// Namespace targets of every `using namespace X;` directive lexically
444    /// visible at this point in the file (declaration order), threaded
445    /// forward sibling-by-sibling by the sequential container walk (see
446    /// `CppWork::Siblings`). An out-of-line member definition written as a
447    /// bare `Class::method` at file/namespace scope with no enclosing
448    /// `namespace {}` block (issue #1093, e.g. log4cxx's
449    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
450    /// const { ... }`) has no other structural signal for which namespace
451    /// actually owns `Class`; this is the best-effort candidate list used to
452    /// recover it so the definition's indexed identity matches its header
453    /// declaration's.
454    visible_using_namespaces: Vec<String>,
455    /// The block scope a C++ function-local class is written directly in.
456    /// Only the local-type walk sets it, on the scope it hands one local
457    /// class; `class_unit` takes precedence, and the class's own body scope
458    /// clears it, so members and nested classes keep their ordinary owner.
459    local_block: Option<CppLocalBlock>,
460    /// Every namespace-scope `using ns::Type;` declaration lexically visible
461    /// at this point, threaded forward like `visible_using_namespaces`. Each
462    /// scope clones this list for every sibling it visits while only a using
463    /// declaration appends to it, so the list is shared copy-on-write.
464    visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
465}
466
467/// One `using ns::Type;` declaration at namespace scope: the name it
468/// introduces and the canonical namespace of the entity it names.
469///
470/// An out-of-line member definition whose owner qualifier starts with that
471/// name (`void Type::method() {}`) defines a member of `ns::Type`; the local
472/// spelling never creates a second owner (#6902 item 4).
473#[derive(Clone, Debug, PartialEq, Eq)]
474pub struct CppUsingDeclarationBinding {
475    /// The `::`-joined package of the namespace scope that holds the using
476    /// declaration. A binding proves an owner only for definitions in this
477    /// same scope: an inner namespace may declare its own class with the same
478    /// name, which per-file extraction cannot rule out.
479    scope_package: String,
480    /// The introduced name, the target's terminal component.
481    name: String,
482    /// The `::`-joined namespace of the target.
483    target_package: String,
484}
485
486/// The lexical identity of one block in a callable body: the callable's own
487/// identity plus a `block@line:column` segment at the block's opening brace.
488/// The coordinate is what separates same-named local classes in overloads
489/// (which share the callable's FqName) and in sibling blocks, without
490/// flattening the class into its enclosing namespace.
491#[derive(Clone)]
492struct CppLocalBlock {
493    callable: CodeUnit,
494    fq: FqName,
495    short_name: String,
496}
497
498impl CppLocalBlock {
499    fn new(callable: &CodeUnit, block: Node<'_>) -> Self {
500        let position = block.start_position();
501        let segment = format!("block@{}:{}", position.row + 1, position.column + 1);
502        Self {
503            fq: callable
504                .fq()
505                .clone()
506                .with_pushed(segment_interner().intern(&segment, SegmentKind::Nested)),
507            short_name: cpp_join_nested_short(callable.short_name(), &segment),
508            callable: callable.clone(),
509        }
510    }
511}
512
513struct CppContainer<'tree> {
514    node: Node<'tree>,
515    scope: ScopeInfo,
516}
517
518struct CppNodeWork<'tree> {
519    node: Node<'tree>,
520    scope: ScopeInfo,
521}
522
523/// Cursor over one container's remaining named children, processed one at a
524/// time (rather than all at once) so a `using namespace X;` sibling can
525/// update `scope.visible_using_namespaces` for the siblings that follow it,
526/// matching real C++ using-directive semantics. Nested container work is
527/// still pushed and fully drained before the cursor resumes (stack LIFO
528/// order), preserving the original left-to-right visitation order.
529struct CppSiblingsWork<'tree> {
530    children: std::vec::IntoIter<Node<'tree>>,
531    scope: ScopeInfo,
532}
533
534enum CppWork<'tree> {
535    Container(CppContainer<'tree>),
536    Node(CppNodeWork<'tree>),
537    Siblings(CppSiblingsWork<'tree>),
538}
539
540fn class_like_name<'tree>(
541    node: Node<'tree>,
542    source: &str,
543    ancestry: &ParentIndex<'tree>,
544) -> Option<String> {
545    let best = class_like_name_from_children(node, source);
546    if let Some(parent) = ancestry.parent(node)
547        && matches!(
548            parent.kind(),
549            "declaration" | "field_declaration" | "function_definition"
550        )
551        // A class_specifier carrying its own body proves the grammar name is
552        // the real class name: a sibling declarator then declares an object
553        // (`class X {} x;`), never a displaced class name. The gate matters
554        // when the class name is itself an all-caps token (`X`, `API`) --
555        // without it the export-macro re-read below steals the object
556        // declarator's name for the class (#2283). The genuine export-macro
557        // shapes leave the class_specifier bodyless, the same invariant
558        // recover_malformed_exported_multiple_base_class already gates on.
559        && cpp_body_node(node).is_none()
560        && node
561            .child_by_field_name("name")
562            .map(|name_node| {
563                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
564            })
565            .unwrap_or(false)
566        && let Some(recovered) = exported_class_name_from_node(parent, source)
567        && best.as_deref() != Some(recovered.as_str())
568    {
569        return Some(recovered);
570    }
571    best.or_else(|| {
572        node.child_by_field_name("name")
573            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
574            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
575    })
576}
577
578fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
579    let mut grammar_name = None;
580    if let Some(name_node) = node.child_by_field_name("name") {
581        let name = normalize_cpp_whitespace(node_text(name_node, source));
582        if name.is_empty() {
583            return None;
584        }
585        if cpp_body_node(node).is_some() || !cpp_export_macro_token(&name) {
586            return Some(name);
587        }
588        grammar_name = Some(name);
589    }
590
591    let mut best = None;
592    let mut cursor = node.walk();
593    let mut stack = Vec::new();
594    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
595        if matches!(
596            child.kind(),
597            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
598        ) {
599            break;
600        }
601        stack.push(child);
602    }
603
604    while let Some(current) = stack.pop() {
605        // Native attributes decorate the class; their identifiers cannot name it.
606        if current.kind() == "attribute_declaration" {
607            continue;
608        }
609        if matches!(current.kind(), "type_identifier" | "identifier") {
610            let name = normalize_cpp_whitespace(node_text(current, source));
611            if !name.is_empty() && !cpp_export_macro_token(&name) {
612                best = Some(name);
613            }
614            continue;
615        }
616
617        push_named_children_reversed(current, &mut stack);
618    }
619    best.or(grammar_name)
620}
621
622pub fn cpp_export_macro_token(token: &str) -> bool {
623    token
624        .chars()
625        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
626}
627
628struct RecoveredExportedClass<'tree> {
629    declaration_node: Node<'tree>,
630    name: String,
631    body: Option<Node<'tree>>,
632    raw_supertypes: Option<Vec<String>>,
633    base_facts: Option<Vec<CppBaseSpecifierFact>>,
634    uses_initializer_body: bool,
635    /// Present only for the fragmented multiple-base export shape (issue #938).
636    /// Carries the true class-body byte region -- the members tree-sitter scattered
637    /// out of the recovered node -- so they can be reparsed and re-owned as members
638    /// rather than lost inside the truncated `initializer_list` stand-in.
639    fragmented_body: Option<FragmentedExportBody>,
640}
641
642struct RecoveredFunctionLikeExportClassPair {
643    name: String,
644    range: Range,
645    raw_supertypes: Option<Vec<String>>,
646    base_facts: Option<Vec<CppBaseSpecifierFact>>,
647    fragmented_body: FragmentedExportBody,
648}
649
650struct RecoveredEmbeddedFunctionLikeExportClass {
651    name: String,
652    range: Range,
653    raw_supertypes: Vec<String>,
654    base_facts: Vec<CppBaseSpecifierFact>,
655    fragmented_body: FragmentedExportBody,
656}
657
658struct RecoveredFunctionLikeExportClass {
659    name: String,
660    range: Range,
661    raw_supertypes: Option<Vec<String>>,
662    base_facts: Option<Vec<CppBaseSpecifierFact>>,
663    member_outcome: Option<FragmentedExportMembers>,
664}
665
666/// The recovered class-body geometry for a fragmented multiple-base export class.
667/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
668/// verbatim for a region reparse (issue #941 machinery) so every recovered member
669/// keeps its exact original byte/line position. `class_range` is the full class
670/// navigation range spanning to the displaced closing brace.
671struct FragmentedExportBody {
672    reparse_start: usize,
673    reparse_end: usize,
674    class_range: Range,
675}
676
677fn recovered_fragmented_export_body(
678    body: Node<'_>,
679    class_range: Range,
680) -> Option<FragmentedExportBody> {
681    let open = body.child(0).filter(|child| child.kind() == "{")?;
682    let close = body
683        .child(body.child_count().saturating_sub(1))
684        .filter(|child| child.kind() == "}" && !child.is_missing());
685    Some(FragmentedExportBody {
686        reparse_start: open.end_byte(),
687        // A zero-width missing `}` contributes no source byte. Keep the whole
688        // body range in that case; subtracting one byte would discard the last
689        // member's semicolon and make the otherwise valid region unsafe to
690        // index. A real close token is excluded by its structured start.
691        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
692        class_range,
693    })
694}
695
696struct DisplacedFragmentNamespaceBoundary<'tree> {
697    class_close: Node<'tree>,
698    class_semicolon: Node<'tree>,
699    namespace_items: Vec<Node<'tree>>,
700}
701
702/// Result of validating a reparsed fragmented class body.  A complete tree can
703/// safely consume the whole region.  A partial tree may contain only the exact
704/// class-named constructor that tree-sitter merged into an access label; its
705/// remaining siblings must stay on the ordinary outer walk.
706enum FragmentedExportMembers {
707    Complete(Tree),
708    ConditionalConstructor(Tree),
709}
710
711#[derive(Clone, Copy)]
712struct DisplacedMacroClassTail {
713    split_index: usize,
714    class_range: Range,
715}
716
717fn recover_exported_class_declaration<'tree>(
718    node: Node<'tree>,
719    source: &str,
720) -> Option<RecoveredExportedClass<'tree>> {
721    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
722        return Some(recovered);
723    }
724
725    let class_node = first_class_like_child(node)?;
726    if let Some(name_node) = class_node.child_by_field_name("name") {
727        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
728        if cpp_export_macro_token(&class_name) {
729            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
730            // Name declarator. Only a bare declarator can be the displaced class name;
731            // wrappers describe an object whose type merely happens to look macro-like.
732            let mut cursor = node.walk();
733            if node
734                .children_by_field_name("declarator", &mut cursor)
735                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
736            {
737                return None;
738            }
739        } else if has_direct_cpp_declarator(node) {
740            return None;
741        }
742    }
743    let name = exported_class_name_from_node(class_node, source)?;
744    Some(RecoveredExportedClass {
745        declaration_node: class_node,
746        name,
747        body: cpp_body_node(class_node),
748        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
749            .then(|| extract_cpp_supertypes(class_node, source)),
750        base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
751            .then(|| extract_cpp_base_facts(class_node, source)),
752        uses_initializer_body: false,
753        fragmented_body: None,
754    })
755}
756
757fn recover_malformed_exported_base_class<'tree>(
758    node: Node<'tree>,
759    source: &str,
760) -> Option<RecoveredExportedClass<'tree>> {
761    if node.kind() != "declaration" {
762        return None;
763    }
764    let class_node = node.child_by_field_name("type")?;
765    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
766        return None;
767    }
768    let macro_name = class_node
769        .child_by_field_name("name")
770        .and_then(|name| direct_identifier_name(name, source))?;
771    if !cpp_export_macro_token(&macro_name) {
772        return None;
773    }
774
775    let mut named_cursor = node.walk();
776    let mut named = node.named_children(&mut named_cursor);
777    // Object-like macro lines before the head are provenance modifiers under
778    // the macro-declaration grammar; the class node follows them.
779    let head_matches_class = loop {
780        let child = named.next()?;
781        if child.kind() == "macro_modifier" {
782            continue;
783        }
784        break same_node(child, class_node);
785    };
786    if !head_matches_class {
787        return None;
788    }
789    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
790    if displaced.kind() == "init_declarator" {
791        // With a single base and an empty body the entire malformed head is
792        // the initializer's declarator, rather than a sibling of it.
793        if named.next().is_some() {
794            return None;
795        }
796        let declarator = displaced.child_by_field_name("declarator")?;
797        let (name, base) = recovered_function_like_export_class_owner_node(declarator, source)?;
798        let body = displaced.child_by_field_name("value")?;
799        if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
800            return None;
801        }
802        return Some(RecoveredExportedClass {
803            declaration_node: node,
804            name,
805            body: Some(body),
806            raw_supertypes: Some(vec![recovered_malformed_base_name(base, source)?]),
807            base_facts: Some(vec![recovered_base_specifier(
808                base,
809                source,
810                recovered_base_is_virtual(declarator, base, source),
811            )?]),
812            uses_initializer_body: true,
813            fragmented_body: fragmented_export_body_region(node, body, source),
814        });
815    }
816    if displaced.kind() == "parenthesized_declarator" {
817        // The macro-declaration grammar keeps the invocation, the displaced
818        // class name and the first base inside a `parenthesized_declarator`,
819        // and the remaining bases beside the body in the trailing
820        // `init_declarator`.
821        let (name, first_base_node) =
822            recovered_function_like_export_class_owner_node(displaced, source)?;
823        let remaining = named.collect::<Vec<_>>();
824        let [init] = remaining.as_slice() else {
825            return None;
826        };
827        if init.kind() != "init_declarator" {
828            return None;
829        }
830        let mut final_base_node = init.child_by_field_name("declarator")?;
831        while final_base_node.kind() == "macro_modifier_declarator" {
832            final_base_node = final_base_node.child_by_field_name("declarator")?;
833        }
834        let first_base = recovered_malformed_base_name(first_base_node, source)?;
835        let final_base = recovered_malformed_base_name(final_base_node, source)?;
836        let body = init.child_by_field_name("value")?;
837        if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
838            return None;
839        }
840        return Some(RecoveredExportedClass {
841            declaration_node: node,
842            name,
843            body: Some(body),
844            raw_supertypes: Some(vec![first_base, final_base]),
845            base_facts: Some(vec![
846                recovered_base_specifier(
847                    first_base_node,
848                    source,
849                    recovered_base_is_virtual(displaced, first_base_node, source),
850                )?,
851                recovered_base_specifier(
852                    final_base_node,
853                    source,
854                    recovered_base_is_virtual(*init, final_base_node, source),
855                )?,
856            ]),
857            uses_initializer_body: true,
858            fragmented_body: fragmented_export_body_region(node, body, source),
859        });
860    }
861    if displaced.kind() != "ERROR" {
862        return None;
863    }
864    let name = displaced_exported_class_name(displaced, source)?;
865
866    let remaining = named.collect::<Vec<_>>();
867    let init = *remaining.last()?;
868    if init.kind() != "init_declarator" {
869        return None;
870    }
871    let final_base = init
872        .child_by_field_name("declarator")
873        .and_then(|base| recovered_malformed_base_name(base, source))?;
874    let body = init.child_by_field_name("value")?;
875    // A complete reduction has a real closing brace here. In Chromium's Widget
876    // declaration, tree-sitter instead emits the same direct `}` slot as a
877    // zero-width missing node where the first body macro truncates the prefix.
878    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
879        return None;
880    }
881
882    if remaining[..remaining.len() - 1]
883        .iter()
884        .any(|child| match child.kind() {
885            "qualified_identifier"
886            | "scoped_type_identifier"
887            | "type_identifier"
888            | "identifier"
889            | "macro_modifier_declarator" => false,
890            "ERROR" => !is_malformed_inheritance_access(*child, source),
891            _ => true,
892        })
893    {
894        return None;
895    }
896
897    let mut raw_supertypes = Vec::new();
898    let mut base_facts = Vec::new();
899    for base in &remaining[..remaining.len() - 1] {
900        if base.kind() == "ERROR" {
901            continue;
902        }
903        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
904        base_facts.push(recovered_base_specifier(
905            *base,
906            source,
907            recovered_base_is_virtual(node, *base, source),
908        )?);
909    }
910    raw_supertypes.push(final_base);
911    let final_base_node = init.child_by_field_name("declarator")?;
912    base_facts.push(recovered_base_specifier(
913        final_base_node,
914        source,
915        recovered_base_is_virtual(init, final_base_node, source),
916    )?);
917
918    Some(RecoveredExportedClass {
919        declaration_node: node,
920        name,
921        body: Some(body),
922        raw_supertypes: Some(raw_supertypes),
923        base_facts: Some(base_facts),
924        uses_initializer_body: true,
925        fragmented_body: fragmented_export_body_region(node, body, source),
926    })
927}
928
929/// Locate the true class-body region for a fragmented multiple-base export class.
930///
931/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
932/// emits in place of the real class body. Tree-sitter reduces that body in one of
933/// two shapes, both of which lose the members from the recovered node:
934///
935/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
936///   a real closing brace and holds the whole body text inline. The interior between
937///   the braces reparses to the members directly.
938/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
939///   first member with a zero-width MISSING `}`; every later member -- and the real
940///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
941///   siblings. The interior runs from the opening brace to that displaced `}`.
942///
943/// Returns the interior byte range to reparse plus the full class navigation range.
944fn fragmented_export_body_region(
945    node: Node<'_>,
946    body: Node<'_>,
947    source: &str,
948) -> Option<FragmentedExportBody> {
949    let reparse_start = body.start_byte() + 1;
950    let close = direct_close_brace(body)?;
951    if close.end_byte() > close.start_byte() {
952        return Some(FragmentedExportBody {
953            reparse_start,
954            reparse_end: close.start_byte(),
955            class_range: cpp_declaration_range(node),
956        });
957    }
958    // The closing brace was displaced past the recovered node. A balanced nested
959    // class keeps its own braces, so the first lone-`}` sibling is this class's.
960    let mut sibling = node.next_named_sibling();
961    let displaced_close = loop {
962        let Some(current) = sibling else {
963            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
964        };
965        if cpp_is_stray_close_brace(current, source) {
966            break current;
967        }
968        sibling = current.next_named_sibling();
969    };
970    Some(FragmentedExportBody {
971        reparse_start,
972        reparse_end: displaced_close.start_byte(),
973        class_range: Range {
974            start_byte: node.start_byte(),
975            end_byte: displaced_close.end_byte(),
976            start_line: node.start_position().row + 1,
977            end_line: displaced_close.end_position().row + 1,
978        },
979    })
980}
981
982/// Locate the true class-body region for the export-macro class shape that
983/// tree-sitter promotes to a `function_definition`.
984///
985/// In this shape the synthetic function body closes at the first inline
986/// method, while the class's real members continue as root-level siblings until
987/// a stray `}` followed by the displaced class `;`. Reparse the complete
988/// interior so those siblings are visited with the recovered class scope.
989fn fragmented_export_function_body_region(
990    node: Node<'_>,
991    body: Node<'_>,
992    source: &str,
993    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
994) -> Option<FragmentedExportBody> {
995    let reparse_start = body.start_byte().checked_add(1)?;
996    if let Some(boundary) = displaced_namespace {
997        return Some(FragmentedExportBody {
998            reparse_start,
999            reparse_end: boundary.class_close.start_byte(),
1000            class_range: Range {
1001                start_byte: node.start_byte(),
1002                end_byte: boundary.class_semicolon.end_byte(),
1003                start_line: node.start_position().row + 1,
1004                end_line: boundary.class_semicolon.end_position().row + 1,
1005            },
1006        });
1007    }
1008    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1009    let boundary =
1010        fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
1011    let boundary_index = boundary.and_then(|boundary| {
1012        siblings
1013            .iter()
1014            .position(|candidate| same_node(*candidate, boundary))
1015    });
1016    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
1017    let mut sibling_index = 0;
1018    // A complete recovered class's synthetic wrapper is immediately followed
1019    // by its displaced semicolon (comments and a trailing attribute macro --
1020    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
1021    // may sit between the body and that semicolon). Only scan for a later
1022    // stray close when real member siblings intervene; otherwise every earlier
1023    // complete class would borrow the next malformed class's close and claim
1024    // its members. The trailing-attribute case is the gtest shape: the scan
1025    // borrowed a close ~1900 lines later and re-owned a following
1026    // `namespace testing { namespace internal {` block as class members,
1027    // doubling the package path ("testing::internal::testing::internal") and
1028    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
1029    // boundary assert (#2297).
1030    while let Some(current) = siblings.get(sibling_index).copied() {
1031        if current.kind() == "comment" {
1032            sibling_index += 1;
1033            continue;
1034        }
1035        if is_trailing_attribute_macro_sibling(current) {
1036            sibling_index += 1;
1037            continue;
1038        }
1039        if cpp_is_stray_semicolon(current, source) {
1040            return None;
1041        }
1042        break;
1043    }
1044    while let Some(current) = siblings.get(sibling_index).copied() {
1045        let next = siblings.get(sibling_index + 1).copied();
1046        if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
1047            return Some(FragmentedExportBody {
1048                reparse_start,
1049                reparse_end: current.start_byte(),
1050                class_range: Range {
1051                    start_byte: node.start_byte(),
1052                    end_byte: semicolon.end_byte(),
1053                    start_line: node.start_position().row + 1,
1054                    end_line: semicolon.end_position().row + 1,
1055                },
1056            });
1057        }
1058        // When the final access label keeps the class close in its malformed
1059        // declaration body, tree-sitter nests the lone `}` ERROR below the
1060        // label instead of exposing it as a direct sibling. Search only the
1061        // scattered siblings after the synthetic wrapper. The first such
1062        // close is the class terminator because nested class bodies retain
1063        // their own balanced class_specifier nodes.
1064        if current.start_byte() >= body.end_byte()
1065            && let Some(close) = cpp_nested_stray_close_brace(current, source)
1066        {
1067            return Some(FragmentedExportBody {
1068                reparse_start,
1069                reparse_end: close.start_byte(),
1070                class_range: Range {
1071                    start_byte: node.start_byte(),
1072                    end_byte: current.end_byte(),
1073                    start_line: node.start_position().row + 1,
1074                    end_line: current.end_position().row + 1,
1075                },
1076            });
1077        }
1078        sibling_index += 1;
1079    }
1080    boundary.map(|boundary| FragmentedExportBody {
1081        reparse_start,
1082        reparse_end: boundary.start_byte(),
1083        class_range: Range {
1084            start_byte: node.start_byte(),
1085            end_byte: boundary.start_byte(),
1086            start_line: node.start_position().row + 1,
1087            end_line: boundary.start_position().row + 1,
1088        },
1089    })
1090}
1091
1092/// Find a later macro-export class that tree-sitter lifted through an enclosing
1093/// preprocessor container. A class that is still a direct sibling can be a
1094/// nested member of the current fragmented class, so only a changed parent is
1095/// a proven boundary between the two recovered class envelopes.
1096fn fragmented_export_sibling_class_boundary<'tree>(
1097    node: Node<'tree>,
1098    source: &str,
1099    ancestry: &ParentIndex<'tree>,
1100) -> Option<Node<'tree>> {
1101    let node_parent = ancestry.parent(node)?;
1102    cpp_following_named_siblings(node, source, ancestry)
1103        .into_iter()
1104        .find(|candidate| {
1105            recover_exported_class_function_definition(*candidate, source).is_some()
1106                && ancestry
1107                    .parent(*candidate)
1108                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
1109        })
1110}
1111
1112/// A trailing attribute macro after a recovered class's closing brace, spelled
1113/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
1114/// bare identifier is never a class member (members need a type), so this
1115/// sibling can only be the class's own tail (#2297).
1116fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
1117    if node.kind() != "expression_statement" {
1118        return false;
1119    }
1120    let mut cursor = node.walk();
1121    let mut children = node.named_children(&mut cursor);
1122    children
1123        .next()
1124        .is_some_and(|child| child.kind() == "identifier")
1125        && children.next().is_none()
1126}
1127
1128/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
1129/// export-class wrapper can place the class close inside an access-label node,
1130/// so direct-sibling checks alone miss the boundary.  Walk named CST children
1131/// only; the helper does not inspect source text beyond the existing structured
1132/// stray-brace predicate.
1133fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1134    let mut stack = vec![node];
1135    while let Some(current) = stack.pop() {
1136        if cpp_is_stray_close_brace(current, source) {
1137            return Some(current);
1138        }
1139        let mut cursor = current.walk();
1140        stack.extend(current.named_children(&mut cursor));
1141    }
1142    None
1143}
1144
1145/// Return named siblings that follow `node`, including siblings that tree-sitter
1146/// attached to an enclosing container after malformed recovery split the local
1147/// declaration list. Stop at the first structurally visible class close so a
1148/// later namespace or exported class cannot supply the recovery boundary.
1149///
1150/// The climb takes a [`ParentIndex`] because it runs once per visited node and
1151/// walks every level to the root: with `Node::parent`, which re-descends from
1152/// the root on each call, a translation unit holding thousands of siblings --
1153/// generated instruction tables, for instance -- made this the whole cost of
1154/// indexing the file. Callers inside the declaration walk pass the walk's
1155/// index; the four that ask from outside a traversal pass
1156/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
1157/// behaviour is unchanged.
1158fn cpp_following_named_siblings<'tree>(
1159    node: Node<'tree>,
1160    source: &str,
1161    ancestry: &ParentIndex<'tree>,
1162) -> Vec<Node<'tree>> {
1163    let mut siblings = Vec::new();
1164    let mut anchor = node;
1165    while let Some(parent) = ancestry.parent(anchor) {
1166        let at_translation_unit = parent.kind() == "translation_unit";
1167        let mut cursor = parent.walk();
1168        assert!(
1169            cursor.goto_first_child(),
1170            "ParentIndex returned a parent without children"
1171        );
1172        while !same_node(cursor.node(), anchor) {
1173            assert!(
1174                cursor.goto_next_sibling(),
1175                "ParentIndex parent does not contain its child anchor"
1176            );
1177        }
1178        while cursor.goto_next_sibling() {
1179            let current = cursor.node();
1180            if !current.is_named() {
1181                continue;
1182            }
1183            if at_translation_unit
1184                && (current.kind() == "namespace_definition"
1185                    || (current.kind() == "function_definition"
1186                        && first_class_like_child(current).is_some()))
1187            {
1188                return siblings;
1189            }
1190            siblings.push(current);
1191            if cpp_is_stray_close_brace(current, source) {
1192                if let Some(semicolon) = current
1193                    .next_named_sibling()
1194                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1195                {
1196                    siblings.push(semicolon);
1197                }
1198                return siblings;
1199            }
1200            if current.start_byte() >= node.end_byte()
1201                && matches!(current.kind(), "ERROR" | "labeled_statement")
1202                && cpp_nested_stray_close_brace(current, source).is_some()
1203            {
1204                return siblings;
1205            }
1206        }
1207        anchor = parent;
1208    }
1209    siblings
1210}
1211
1212fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1213    if node.start_byte() >= class_end {
1214        return false;
1215    }
1216    node.end_byte() <= class_end
1217        || cpp_nested_stray_close_brace(node, source)
1218            .is_some_and(|close| close.start_byte() == class_end)
1219}
1220
1221/// A class whose head tree-sitter kept in one malformed container while the
1222/// members after the first and the closing `};` scattered across that
1223/// container's following siblings.
1224struct FragmentedClassRecovery<'tree> {
1225    declaration_node: Node<'tree>,
1226    name: String,
1227    raw_supertypes: Vec<String>,
1228    body: FragmentedExportBody,
1229}
1230
1231/// Recover a class whose opening prefix is retained in one ERROR node while one
1232/// or more nested class closes and the outer close are displaced to sibling
1233/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
1234/// tokens establish nesting depth and the displaced close nodes terminate it.
1235///
1236/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
1237/// `class` keyword as the container's first token. A function-like export macro
1238/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
1239/// body-less `class_specifier` named after the macro followed by the
1240/// invocation's `(`, arguments and `)`, with the real class name and the base
1241/// clause left as bare tokens after it (#2924).
1242fn fragmented_class_body<'tree>(
1243    node: Node<'tree>,
1244    source: &str,
1245) -> Option<FragmentedClassRecovery<'tree>> {
1246    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1247        return Some(recovered);
1248    }
1249    let supported_container = node.kind() == "ERROR"
1250        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1251    if !supported_container {
1252        return None;
1253    }
1254    let mut cursor = node.walk();
1255    let children = node.children(&mut cursor).collect::<Vec<_>>();
1256    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1257        return Some(recovered);
1258    }
1259    let keyword = children.first()?;
1260    if !matches!(keyword.kind(), "class" | "struct" | "union") {
1261        return None;
1262    }
1263    let name_node = children
1264        .iter()
1265        .copied()
1266        .skip(1)
1267        .find(|child| child.is_named())?;
1268    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1269        return None;
1270    }
1271    let name = normalize_cpp_whitespace(node_text(name_node, source));
1272    if name.is_empty() || cpp_export_macro_token(&name) {
1273        return None;
1274    }
1275    let open_index = children.iter().position(|child| child.kind() == "{")?;
1276    let open = children[open_index];
1277    let body = displaced_fragment_namespace_geometry(node, source)
1278        .map(|boundary| FragmentedExportBody {
1279            reparse_start: open.end_byte(),
1280            reparse_end: boundary.class_close.start_byte(),
1281            class_range: Range {
1282                start_byte: node.start_byte(),
1283                end_byte: boundary.class_semicolon.end_byte(),
1284                start_line: node.start_position().row + 1,
1285                end_line: boundary.class_semicolon.end_position().row + 1,
1286            },
1287        })
1288        .or_else(|| fragmented_displaced_class_body(node, &children, open_index, source))?;
1289    Some(FragmentedClassRecovery {
1290        declaration_node: node,
1291        name,
1292        raw_supertypes: extract_cpp_supertypes(node, source),
1293        body,
1294    })
1295}
1296
1297/// The fragmented body of a function-like export-macro class head that
1298/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
1299/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
1300/// public virtual EC_PublicKey {`, #2924).
1301///
1302/// The class name is read positionally by [`recovered_export_head_name`] -- the
1303/// last identifier before the head ends at `final`, at the base clause `:`, or
1304/// at the body `{` -- so no macro spelling is interpreted, and a base clause
1305/// spelled with `virtual`, with `final`, or with qualified base names needs no
1306/// arm of its own.
1307fn fragmented_export_macro_class_body<'tree>(
1308    node: Node<'tree>,
1309    children: &[Node<'tree>],
1310    source: &str,
1311) -> Option<FragmentedClassRecovery<'tree>> {
1312    let class_node = *children.first()?;
1313    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1314        return None;
1315    }
1316    // The identifier tree-sitter took for the class name is the export macro, a
1317    // function-like invocation when the `(` of its argument list follows it
1318    // directly. Spelling plays no part (#2557).
1319    class_node
1320        .child_by_field_name("name")
1321        .and_then(|name| direct_identifier_name(name, source))?;
1322    let invocation = *children.get(1)?;
1323    if invocation.is_named() || invocation.kind() != "(" {
1324        return None;
1325    }
1326    let open_index = children
1327        .iter()
1328        .position(|child| !child.is_named() && child.kind() == "{")?;
1329    let open = children[open_index];
1330    let name_node = recovered_export_head_name(node, open, source)?;
1331    let name = normalize_cpp_whitespace(node_text(name_node, source));
1332    if name.is_empty() {
1333        return None;
1334    }
1335    Some(FragmentedClassRecovery {
1336        declaration_node: node,
1337        name,
1338        raw_supertypes: recovered_export_head_bases(
1339            node,
1340            name_node.end_byte(),
1341            open.start_byte(),
1342            source,
1343        ),
1344        body: fragmented_displaced_class_body(node, children, open_index, source)
1345            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1346    })
1347}
1348
1349/// The class body a fragmented head opened at `children[open_index]` but could
1350/// not close: tree-sitter scattered the remaining members and the closing `};`
1351/// across the container's following siblings. The direct class tokens after the
1352/// opening brace say how many closes the nested classes consume before this
1353/// class's own, and the displaced `}`/`;` sibling pair terminates it.
1354fn fragmented_displaced_class_body(
1355    node: Node<'_>,
1356    children: &[Node<'_>],
1357    open_index: usize,
1358    source: &str,
1359) -> Option<FragmentedExportBody> {
1360    let open = children[open_index];
1361    let nested_class_opens = children[open_index + 1..]
1362        .iter()
1363        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1364        .count();
1365    let mut closes_remaining = 1 + nested_class_opens;
1366    let mut sibling = node.next_named_sibling();
1367    while let Some(candidate) = sibling {
1368        let next = candidate.next_named_sibling();
1369        // The class's own close: a lone `}` `ERROR` the mis-parse split off,
1370        // or -- under the macro-declaration grammar, which keeps the fragmented
1371        // head in an `ERROR` and the close as a plain child of the enclosing
1372        // list -- a bare `}` there. The `;` after it is what proves the close
1373        // is the class's own rather than the enclosing scope's.
1374        let is_close = cpp_is_stray_close_brace(candidate, source)
1375            || (candidate.kind() == "}" && !candidate.is_named());
1376        if is_close {
1377            closes_remaining -= 1;
1378            if closes_remaining == 0 {
1379                let semicolon = next.filter(|next| cpp_is_stray_semicolon(*next, source))?;
1380                if open.end_byte() >= candidate.start_byte() {
1381                    return None;
1382                }
1383                return Some(FragmentedExportBody {
1384                    reparse_start: open.end_byte(),
1385                    reparse_end: candidate.start_byte(),
1386                    class_range: Range {
1387                        start_byte: node.start_byte(),
1388                        end_byte: semicolon.end_byte(),
1389                        start_line: node.start_position().row + 1,
1390                        end_line: semicolon.end_position().row + 1,
1391                    },
1392                });
1393            }
1394        }
1395        sibling = next;
1396    }
1397    None
1398}
1399
1400/// The body of a fragmented export-macro class head whose close tree-sitter
1401/// spent on the enclosing container instead: Botan's
1402/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1403/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1404/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1405/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1406/// (#2924).
1407fn fragmented_container_close_class_body(
1408    node: Node<'_>,
1409    open: Node<'_>,
1410    source: &str,
1411) -> Option<FragmentedExportBody> {
1412    let parent = node.parent()?;
1413    if !matches!(
1414        parent.kind(),
1415        "declaration_list" | "field_declaration_list" | "compound_statement"
1416    ) {
1417        return None;
1418    }
1419    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1420    // The container's close is this class's close only when the body's own
1421    // brace balance says so. Without that cross-check -- the one issue #1524
1422    // already uses on a mis-closed wrapper body -- a class that really does end
1423    // earlier would claim every later container-level declaration as a member.
1424    if OrphanedNamespaceScopeIndex::build(parent, source)
1425        .matching_close_brace(open.start_byte())
1426        .map(|close| close.start_byte)
1427        != Some(close.start_byte())
1428        || open.end_byte() >= close.start_byte()
1429    {
1430        return None;
1431    }
1432    Some(FragmentedExportBody {
1433        reparse_start: open.end_byte(),
1434        reparse_end: close.start_byte(),
1435        class_range: Range {
1436            start_byte: node.start_byte(),
1437            end_byte: close.end_byte(),
1438            start_line: node.start_position().row + 1,
1439            end_line: close.end_position().row + 1,
1440        },
1441    })
1442}
1443
1444pub(crate) fn recovered_fragmented_class_has_body(
1445    node: Node<'_>,
1446    source: &str,
1447    expected_name: &str,
1448    expected_range: &Range,
1449) -> bool {
1450    fragmented_class_body(node, source).is_some_and(|recovered| {
1451        recovered.name == expected_name
1452            && recovered.body.class_range.start_byte == expected_range.start_byte
1453            && recovered.body.class_range.end_byte == expected_range.end_byte
1454    })
1455}
1456
1457/// Recover a plain class whose parser-visible body ends inside a malformed
1458/// inline member. Tree-sitter then attaches either the next real member
1459/// declarator or the unfinished `else` branch directly to the outer function
1460/// definition and leaves the class's actual `};` among later siblings. Those
1461/// structured continuations and the close/semicolon siblings establish the
1462/// complete body envelope without interpreting source text.
1463fn fragmented_plain_class_declaration_body<'tree>(
1464    node: Node<'tree>,
1465    source: &str,
1466) -> Option<FragmentedClassRecovery<'tree>> {
1467    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1468        return None;
1469    }
1470    let class_node = node.child_by_field_name("type")?;
1471    if !matches!(
1472        class_node.kind(),
1473        "class_specifier" | "struct_specifier" | "union_specifier"
1474    ) {
1475        return None;
1476    }
1477    let name_node = class_node.child_by_field_name("name")?;
1478    let name = normalize_cpp_whitespace(node_text(name_node, source));
1479    if name.is_empty() || cpp_export_macro_token(&name) {
1480        return None;
1481    }
1482    let body = cpp_body_node(class_node)?;
1483    if body.kind() != "field_declaration_list" {
1484        return None;
1485    }
1486    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1487        if declarator.start_byte() < class_node.end_byte() {
1488            return None;
1489        }
1490        let mut cursor = node.walk();
1491        node.named_children(&mut cursor).any(|child| {
1492            if child.kind() != "ERROR"
1493                || child.start_byte() < class_node.end_byte()
1494                || child.end_byte() > declarator.start_byte()
1495            {
1496                return false;
1497            }
1498            let mut cursor = child.walk();
1499            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1500            let Some((return_type, attributes)) = components.split_last() else {
1501                return false;
1502            };
1503            matches!(
1504                return_type.kind(),
1505                "identifier"
1506                    | "type_identifier"
1507                    | "primitive_type"
1508                    | "decltype"
1509                    | "placeholder_type_specifier"
1510            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1511                && attributes.iter().all(|attribute| {
1512                    matches!(attribute.kind(), "identifier" | "type_identifier")
1513                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1514                            *attribute, source,
1515                        )))
1516                })
1517        })
1518    } else {
1519        let mut cursor = node.walk();
1520        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1521        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1522            if same_node(*candidate_class, class_node)
1523                && continuation.kind() == "identifier"
1524                && node_text(*continuation, source) == "else"
1525                && continuation_body.kind() == "compound_statement"
1526                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1527                && continuation_body
1528                    .child(continuation_body.child_count().saturating_sub(1))
1529                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1530    };
1531    if !displaced_member {
1532        return None;
1533    }
1534    let open = body
1535        .children(&mut body.walk())
1536        .find(|child| child.kind() == "{")?;
1537    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1538    let ordinary_boundary =
1539        siblings
1540            .iter()
1541            .copied()
1542            .enumerate()
1543            .find_map(|(close_index, close)| {
1544                cpp_stray_close_followed_by_semicolon(
1545                    close,
1546                    siblings.get(close_index + 1).copied(),
1547                    source,
1548                )
1549                .map(|semicolon| (close, semicolon))
1550            });
1551    let (close, semicolon) =
1552        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1553            (boundary.class_close, boundary.class_semicolon)
1554        } else {
1555            ordinary_boundary?
1556        };
1557    if open.end_byte() >= close.start_byte() {
1558        return None;
1559    }
1560    Some(FragmentedClassRecovery {
1561        declaration_node: class_node,
1562        name,
1563        raw_supertypes: extract_cpp_supertypes(class_node, source),
1564        body: FragmentedExportBody {
1565            reparse_start: open.end_byte(),
1566            reparse_end: close.start_byte(),
1567            class_range: Range {
1568                start_byte: class_node.start_byte(),
1569                end_byte: semicolon.end_byte(),
1570                start_line: class_node.start_position().row + 1,
1571                end_line: semicolon.end_position().row + 1,
1572            },
1573        },
1574    })
1575}
1576
1577fn displaced_export_function_namespace_shape<'tree>(
1578    declaration: Node<'tree>,
1579    source: &str,
1580) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1581    let mut nested = Vec::new();
1582    for index in (0..declaration.named_child_count()).rev() {
1583        nested.push(declaration.named_child(index)?);
1584    }
1585    while let Some(current) = nested.pop() {
1586        // A recovered export class nested in this class can consume the first
1587        // parser-visible namespace close itself. In that shape the existing
1588        // later-class boundary logic already distinguishes the nested and
1589        // namespace-sibling owners; do not mistake the nested close for this
1590        // class's terminator.
1591        if recover_exported_class_function_definition(current, source).is_some() {
1592            return None;
1593        }
1594        for index in (0..current.named_child_count()).rev() {
1595            nested.push(current.named_child(index)?);
1596        }
1597    }
1598    let mut same_envelope_sibling = declaration.next_named_sibling();
1599    while let Some(current) = same_envelope_sibling {
1600        if recover_exported_class_function_definition(current, source).is_some() {
1601            return None;
1602        }
1603        same_envelope_sibling = current.next_named_sibling();
1604    }
1605    let declaration_list = declaration.parent()?;
1606    let (namespace, class_close, trailing_semicolon) =
1607        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1608    // A chain of malformed export classes can consume one parser-visible
1609    // namespace close per class. Walk through the enclosing sibling levels so
1610    // the later real namespace close remains the structural boundary; a
1611    // direct next-sibling walk stops at the first collapsed namespace and
1612    // incorrectly makes its intervening items members of this class.
1613    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1614    let trailing_index = siblings
1615        .iter()
1616        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1617    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1618        recover_exported_class_function_definition(*candidate, source).is_some()
1619    }) {
1620        // Consecutive recovered classes already have an exact sibling-class
1621        // boundary. Preserve that established path, including nested export
1622        // classes, instead of interpreting the first class close as a
1623        // collapsed namespace boundary.
1624        return None;
1625    }
1626    let mut namespace_items = Vec::new();
1627    let mut nested_fragment_end = 0;
1628    for current in siblings.into_iter().skip(trailing_index + 1) {
1629        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1630        {
1631            return Some(DisplacedFragmentNamespaceBoundary {
1632                class_close,
1633                class_semicolon: trailing_semicolon,
1634                namespace_items,
1635            });
1636        }
1637        if current.start_byte() >= nested_fragment_end
1638            && let Some(recovered) = fragmented_class_body(current, source)
1639        {
1640            nested_fragment_end = recovered.body.class_range.end_byte;
1641        } else if current.start_byte() >= nested_fragment_end
1642            && recover_exported_class_function_definition(current, source).is_some()
1643            && let Some(body) = cpp_body_node(current)
1644            && let Some(fragmented) =
1645                fragmented_export_function_body_region(current, body, source, None)
1646        {
1647            nested_fragment_end = fragmented.class_range.end_byte;
1648        }
1649        namespace_items.push(current);
1650    }
1651    None
1652}
1653
1654fn displaced_fragment_namespace_boundary<'tree>(
1655    declaration: Node<'tree>,
1656    body: Node<'tree>,
1657    source: &str,
1658) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1659    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1660    let reparse_start = body.start_byte() + 1;
1661    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1662    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1663}
1664
1665/// Recover the class/namespace brace geometry for a declaration whose class
1666/// close tree-sitter consumed as the enclosing namespace close. This proof is
1667/// independent of whether every member in the class body can be reparsed: the
1668/// ordinary-tree fallback can still re-own bounded sibling declarations when
1669/// an unknown macro makes the complete body reparse unsafe.
1670fn displaced_fragment_namespace_geometry<'tree>(
1671    declaration: Node<'tree>,
1672    source: &str,
1673) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1674    // A templated class's malformed function wrapper remains beneath the
1675    // template node even though its later members have escaped to the
1676    // enclosing declaration list. Lift only that exact declaration child.
1677    let envelope = declaration
1678        .parent()
1679        .filter(|parent| {
1680            parent.kind() == "template_declaration"
1681                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1682        })
1683        .unwrap_or(declaration);
1684    let declaration_list = envelope.parent()?;
1685    let (_, class_close, trailing_semicolon) =
1686        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1687    let mut namespace_items = Vec::new();
1688    let mut sibling = trailing_semicolon.next_named_sibling();
1689    let mut nested_fragment_end = 0;
1690    loop {
1691        let current = sibling?;
1692        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1693        {
1694            break;
1695        }
1696        if current.start_byte() >= nested_fragment_end
1697            && let Some(recovered) = fragmented_class_body(current, source)
1698        {
1699            nested_fragment_end = recovered.body.class_range.end_byte;
1700        }
1701        namespace_items.push(current);
1702        sibling = current.next_named_sibling();
1703    }
1704    Some(DisplacedFragmentNamespaceBoundary {
1705        class_close,
1706        class_semicolon: trailing_semicolon,
1707        namespace_items,
1708    })
1709}
1710
1711fn cpp_fragment_namespace_boundary_nodes<'tree>(
1712    declaration_list: Node<'tree>,
1713) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1714    if declaration_list.kind() != "declaration_list" {
1715        return None;
1716    }
1717    let namespace = declaration_list.parent()?;
1718    if namespace.kind() != "namespace_definition"
1719        || namespace.child_by_field_name("body") != Some(declaration_list)
1720    {
1721        return None;
1722    }
1723    let class_close = direct_close_brace(declaration_list)?;
1724    let trailing_semicolon = namespace.next_named_sibling()?;
1725    (trailing_semicolon.kind() == "expression_statement"
1726        && trailing_semicolon.named_child_count() == 0)
1727        .then_some((namespace, class_close, trailing_semicolon))
1728}
1729
1730/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1731fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1732    (0..node.child_count())
1733        .filter_map(|index| node.child(index))
1734        .find(|child| !child.is_named() && child.kind() == "}")
1735}
1736
1737/// A displaced lone closing brace: the class close that the fragmented multiple-base
1738/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1739fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1740    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1741}
1742
1743fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1744    let mut name = None;
1745    let mut colon_count = 0;
1746    let mut access_count = 0;
1747    for index in 0..node.child_count() {
1748        let child = node.child(index)?;
1749        match child.kind() {
1750            "identifier" | "type_identifier" if child.is_named() => {
1751                if name.is_some() {
1752                    return None;
1753                }
1754                let candidate = normalize_cpp_whitespace(node_text(child, source));
1755                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1756                    return None;
1757                }
1758                name = Some(candidate);
1759            }
1760            "template_function" | "template_type" if child.is_named() => {
1761                if name.is_some() {
1762                    return None;
1763                }
1764                let candidate = child
1765                    .child_by_field_name("name")
1766                    .and_then(|name| direct_identifier_name(name, source))?;
1767                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1768                    return None;
1769                }
1770                name = Some(candidate);
1771            }
1772            ":" if !child.is_named() => colon_count += 1,
1773            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1774            _ => return None,
1775        }
1776    }
1777    (colon_count == 1 && access_count == 1)
1778        .then_some(name)
1779        .flatten()
1780}
1781
1782fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1783    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1784        return false;
1785    }
1786    node.named_child(0)
1787        .and_then(|child| direct_identifier_name(child, source))
1788        .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1789}
1790
1791use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1792
1793fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1794    match node.kind() {
1795        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1796            recovered_base_atom(node, source)
1797        }
1798        // The macro-declaration grammar wraps an access keyword and the base
1799        // as a `macro_modifier_declarator`; the base is its declarator.
1800        "macro_modifier_declarator" => node
1801            .child_by_field_name("declarator")
1802            .and_then(|declarator| recovered_malformed_base_name(declarator, source)),
1803        "template_type" | "template_function" => node
1804            .child_by_field_name("name")
1805            .and_then(|name| recovered_malformed_base_name(name, source)),
1806        "ERROR" => None,
1807        "qualified_identifier" | "scoped_type_identifier" => {
1808            let suffix = node
1809                .child_by_field_name("name")
1810                .and_then(|name| recovered_malformed_base_name(name, source))?;
1811            let scope = node
1812                .child_by_field_name("scope")
1813                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1814            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1815                malformed_qualified_prefix(node, source)?
1816            } else {
1817                if malformed_qualified_prefix(node, source).is_some() {
1818                    return None;
1819                }
1820                scope
1821            };
1822            Some(format!("{prefix}::{suffix}"))
1823        }
1824        _ => None,
1825    }
1826}
1827
1828fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1829    let mut pending = vec![node];
1830    let mut components = Vec::new();
1831    while let Some(current) = pending.pop() {
1832        match current.kind() {
1833            "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1834                components.push(recovered_base_atom(current, source)?);
1835            }
1836            "macro_modifier_declarator" => {
1837                pending.push(current.child_by_field_name("declarator")?);
1838            }
1839            "template_type" | "template_function" => {
1840                pending.push(current.child_by_field_name("name")?);
1841            }
1842            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1843                pending.push(current.child_by_field_name("name")?);
1844                if let Some(scope) = current.child_by_field_name("scope") {
1845                    // Malformed export bases can put the access keyword in
1846                    // `scope` and the real scope in the direct ERROR child.
1847                    // Keep the exact AST recovery used by the declaration.
1848                    if recovered_base_atom(scope, source).is_some_and(|name| {
1849                        matches!(name.as_str(), "public" | "protected" | "private")
1850                    }) {
1851                        pending.push(malformed_qualified_prefix_node(current, source)?);
1852                    } else {
1853                        if malformed_qualified_prefix_node(current, source).is_some() {
1854                            return None;
1855                        }
1856                        pending.push(scope);
1857                    }
1858                }
1859            }
1860            _ => return None,
1861        }
1862    }
1863    Some(components)
1864}
1865
1866fn recovered_base_specifier(
1867    node: Node<'_>,
1868    source: &str,
1869    is_virtual: bool,
1870) -> Option<CppBaseSpecifierFact> {
1871    let components = recovered_base_components(node, source)?;
1872    crate::source_facts::base_specifier_fact(
1873        node,
1874        components,
1875        is_virtual,
1876        CppAccessSpecifier::Unknown,
1877    )
1878}
1879
1880fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1881    // Recovery can put the qualifier in an ERROR sibling or inside the base
1882    // node. Read only the AST prefix through this base, resetting at preceding
1883    // inheritance delimiters so another base's qualifier cannot leak here.
1884    let mut pending = vec![container];
1885    let mut is_virtual = false;
1886    while let Some(node) = pending.pop() {
1887        if node.start_byte() >= base.end_byte() {
1888            break;
1889        }
1890        if node.child_count() != 0 {
1891            let mut cursor = node.walk();
1892            let children = node
1893                .children(&mut cursor)
1894                .take_while(|child| child.start_byte() < base.end_byte())
1895                .collect::<Vec<_>>();
1896            pending.extend(children.into_iter().rev());
1897            continue;
1898        }
1899        if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1900            is_virtual = false;
1901        } else if node.kind() == "virtual"
1902            || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1903        {
1904            is_virtual = true;
1905        }
1906    }
1907    is_virtual
1908}
1909
1910fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1911    if !matches!(
1912        node.kind(),
1913        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1914    ) {
1915        return None;
1916    }
1917    let name = normalize_cpp_whitespace(node_text(node, source));
1918    (!name.is_empty()).then_some(name)
1919}
1920
1921fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1922    malformed_qualified_prefix_node(node, source)
1923        .and_then(|prefix| recovered_base_atom(prefix, source))
1924}
1925
1926fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1927    let mut prefix = None;
1928    let mut cursor = node.walk();
1929    for error in node
1930        .named_children(&mut cursor)
1931        .filter(|child| child.kind() == "ERROR")
1932    {
1933        if prefix.is_some() {
1934            return None;
1935        }
1936        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1937        // the qualified name's scope and leaves `virtual` beside the real
1938        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1939        // component of the base's name (#2924).
1940        let mut error_cursor = error.walk();
1941        let atoms = error
1942            .named_children(&mut error_cursor)
1943            .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1944            .collect::<Option<Vec<_>>>()?;
1945        let [atom] = atoms
1946            .iter()
1947            .filter(|(_, name)| name != "virtual")
1948            .collect::<Vec<_>>()[..]
1949        else {
1950            return None;
1951        };
1952        prefix = Some(atom.0);
1953    }
1954    prefix
1955}
1956
1957type RecoveredFunctionDefinitionClass<'tree> = (
1958    Node<'tree>,
1959    String,
1960    Option<Vec<String>>,
1961    Option<Vec<CppBaseSpecifierFact>>,
1962);
1963/// One declaration an attribute-like macro invocation swallowed into a
1964/// declaration-scope `ERROR`, with the byte range that spells it.
1965/// What [`stranded_declaration_run`] read out of one node.
1966struct StrandedRun<'tree> {
1967    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1968    /// Whether every part of the node read as part of a declaration. False when
1969    /// a part the reader does not understand ended it early, or when the last
1970    /// parts were types with no declarator after them. Callers that index what
1971    /// was found keep the declarations either way; a caller deciding whether a
1972    /// whole region is safe to index requires this.
1973    complete: bool,
1974}
1975
1976struct MacroWrappedDeclaration<'tree> {
1977    declarator: Node<'tree>,
1978    range: Range,
1979    /// Whether the recovered declaration spells `static`. The envelope hides
1980    /// that keyword from the ordinary linkage reader, which looks for it among
1981    /// a declaration node's own children, and internal linkage is what decides
1982    /// whether a header declaration and a body in another file are one symbol.
1983    is_static: bool,
1984}
1985
1986/// Whether `node` stands where declarations live: directly in the translation
1987/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1988/// stands in one of those.
1989///
1990/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1991/// where it stands. The case that matters is a whole translation unit the
1992/// parser could not close: whisper.cpp's `include/whisper.h` opens
1993/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1994/// so the braces never balance in the token stream tree-sitter sees, the file's
1995/// own root node is an `ERROR`, and every top-level declaration in the file is
1996/// a child of it (#3094). Without this the macro recoveries below read nothing
1997/// in such a file, because none of their envelopes is in a declaration scope
1998/// the parser managed to build.
1999fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2000    declaration_scope_container(node, ancestry).is_some()
2001}
2002
2003/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
2004/// end is where a parse failure that starts at `node` can still be doing
2005/// damage: a declaration scope has no synchronization point of its own, so the
2006/// parser carries such a failure to the scope's close.
2007fn declaration_scope_container<'tree>(
2008    node: Node<'tree>,
2009    ancestry: &ParentIndex<'tree>,
2010) -> Option<Node<'tree>> {
2011    let mut parent = ancestry.parent(node)?;
2012    loop {
2013        match parent.kind() {
2014            "translation_unit" => return Some(parent),
2015            "declaration_list" => {
2016                return ancestry
2017                    .parent(parent)
2018                    .is_some_and(|grandparent| {
2019                        matches!(
2020                            grandparent.kind(),
2021                            "namespace_definition" | "linkage_specification"
2022                        )
2023                    })
2024                    .then_some(parent);
2025            }
2026            "ERROR" => match ancestry.parent(parent) {
2027                Some(grandparent) => parent = grandparent,
2028                // A root `ERROR` is the translation unit the parser could not
2029                // build, so its children stand at file scope.
2030                None => return Some(parent),
2031            },
2032            // A preprocessor conditional is a transparent declaration-scope
2033            // wrapper when it sits directly under the translation unit or a
2034            // namespace/linkage declaration list. Keep climbing through it;
2035            // the next ordinary construct still decides whether the enclosed
2036            // error is at declaration scope. In particular, a conditional
2037            // inside a function reaches its compound statement and is
2038            // rejected below rather than becoming a global recovery region.
2039            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
2040                parent = ancestry.parent(parent)?;
2041            }
2042            _ => return None,
2043        }
2044    }
2045}
2046
2047/// Whether `node` is an `ERROR` the parser produced where declarations live.
2048fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2049    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
2050}
2051
2052/// The identifier an object-like macro marker wraps. The grammar admits a lone
2053/// identifier as a `macro_modifier` in a declaration's specifier run, or as
2054/// the prefix of a `macro_modifier_declarator`, only where the declaration
2055/// would otherwise not parse, so the modifier is always the structured reading
2056/// of a macro the workspace never expands: it is provenance about the
2057/// declaration, never a declarator of its own.
2058pub(crate) fn macro_modifier_identifier(node: Node<'_>) -> Option<Node<'_>> {
2059    if node.kind() != "macro_modifier" {
2060        return None;
2061    }
2062    node.named_child(0)
2063        .filter(|child| child.kind() == "identifier")
2064}
2065
2066/// The modifier word and inner declarator a `macro_modifier_declarator`
2067/// wraps: the grammar's structured reading of an unexpanded object-like macro
2068/// before a declarator. In a statement-context class-body reparse the modifier
2069/// can also be the member's return type, which the grammar could not separate
2070/// from the declarator and admitted as a modifier instead.
2071pub(crate) fn macro_modifier_declarator_parts(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
2072    if node.kind() != "macro_modifier_declarator" {
2073        return None;
2074    }
2075    let mut cursor = node.walk();
2076    let modifier = node
2077        .named_children(&mut cursor)
2078        .find(|child| child.kind() == "macro_modifier")
2079        .and_then(macro_modifier_identifier)?;
2080    let declarator = node.child_by_field_name("declarator")?;
2081    Some((modifier, declarator))
2082}
2083
2084/// Whether `node` can only be part of what precedes a declarator -- a type, a
2085/// specifier, or a word of the attribute macro's own text that the lexer left
2086/// as a bare identifier or, under the macro-declaration grammar, wrapped as a
2087/// `macro_modifier` -- so a run of these followed by a declarator is one
2088/// declaration the parser failed to group.
2089fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
2090    matches!(
2091        node.kind(),
2092        "identifier"
2093            | "type_identifier"
2094            | "primitive_type"
2095            | "sized_type_specifier"
2096            | "struct_specifier"
2097            | "union_specifier"
2098            | "enum_specifier"
2099            | "type_qualifier"
2100            | "storage_class_specifier"
2101            | "explicit_function_specifier"
2102            | "virtual_function_specifier"
2103            | "qualified_identifier"
2104            | "template_type"
2105            | "dependent_type"
2106            | "placeholder_type_specifier"
2107            | "macro_modifier"
2108            | "macro_type_specifier"
2109    )
2110}
2111
2112/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
2113/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
2114/// words the lexer reports as bare identifiers. Anything else in such an
2115/// `ERROR` stops the recovery, so a macro argument that carries structure this
2116/// recovery does not understand is never guessed at.
2117fn is_macro_argument_error(node: Node<'_>) -> bool {
2118    if node.kind() != "ERROR" {
2119        return false;
2120    }
2121    let mut cursor = node.walk();
2122    node.named_children(&mut cursor).all(|child| {
2123        matches!(
2124            child.kind(),
2125            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
2126        )
2127    })
2128}
2129
2130/// The end of a recovered declaration: the declarator's end, extended across
2131/// the `;` the grammar left beside it inside the envelope.
2132///
2133/// The envelope's own end is the hard boundary. The parser hands the last
2134/// declaration's `;` to the sibling statement it recovered with, outside the
2135/// envelope, and a range that reached it would no longer lie inside one node --
2136/// which is how every reader (including the resolver's climb from a range to
2137/// the node that declares it) finds a recovered declaration again.
2138fn recovered_declaration_end(declarator: Node<'_>) -> usize {
2139    declarator
2140        .next_sibling()
2141        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
2142        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
2143}
2144
2145/// The declarations `node` holds after an attribute-like macro cost the parser
2146/// their grouping, in source order.
2147///
2148/// The parser packs the parts into whichever slots it has left. In whisper's
2149/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
2150/// field takes the export macro, the real return type and the *next*
2151/// declaration's declarator both end up inside one sibling `ERROR`, and the
2152/// `declarator` field takes whatever declaration the recovery reached last. In
2153/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
2154/// arrive as bare identifiers and the attributed member and the member after it
2155/// share one declarator node.
2156///
2157/// Both are the same failure, so both get the same reading: flatten the node's
2158/// parts -- splicing each nested `ERROR`'s own children in place, since an
2159/// `ERROR` here is only a grouping failure -- and read the flat run as what it
2160/// spells, a run of type-and-specifier tokens followed by a declarator, over
2161/// and over.
2162///
2163/// Fails closed. A part that is neither a declarator nor something that can
2164/// only precede one ends the recovery there, and the declarations before it are
2165/// kept.
2166fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
2167    let mut parts = Vec::new();
2168    let mut cursor = node.walk();
2169    for child in node.named_children(&mut cursor) {
2170        if child.kind() == "ERROR" {
2171            let mut error_cursor = child.walk();
2172            parts.extend(child.named_children(&mut error_cursor));
2173        } else {
2174            parts.push(child);
2175        }
2176    }
2177
2178    let mut declarations = Vec::new();
2179    let mut start = None;
2180    let mut is_static = false;
2181    let mut complete = true;
2182    for part in parts {
2183        if part.kind() == "comment" {
2184            continue;
2185        }
2186        if let Some(declarator) = extract_function_declarator(part) {
2187            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2188            declarations.push(MacroWrappedDeclaration {
2189                declarator,
2190                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2191                is_static,
2192            });
2193            is_static = false;
2194            continue;
2195        }
2196        if !is_recovered_declaration_type_part(part) {
2197            complete = false;
2198            break;
2199        }
2200        is_static |= part.kind() == "storage_class_specifier"
2201            && normalize_cpp_whitespace(node_text(part, source)) == "static";
2202        start.get_or_insert(part.start_byte());
2203    }
2204    StrandedRun {
2205        declarations,
2206        complete: complete && start.is_none(),
2207    }
2208}
2209
2210/// The declarations an attribute-like macro invocation swallowed into a
2211/// declaration-scope `ERROR`, in source order.
2212///
2213/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
2214/// declaration in a macro call:
2215///
2216/// ```text
2217/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
2218///                  struct llama_model * model,
2219///           struct llama_context_params   params),
2220///         "use llama_init_from_model instead");
2221/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
2222/// ```
2223///
2224/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
2225/// holding the macro name, the wrapped declaration as a
2226/// `parameter_declaration`, the hint string as another `ERROR`, and then every
2227/// following declaration as a further `parameter_declaration` until it
2228/// recovers. That is what removed `llama_tokenize` from the index and left the
2229/// seven-argument call in `talk-llama.cpp` with only that file's own
2230/// three-parameter `static` overload to choose from (#2552, and the
2231/// `LLAMA_API` half of #2551).
2232///
2233/// Every part of every swallowed declaration is still a real node; only their
2234/// grouping is lost. This rebuilds the grouping and reads the nodes.
2235///
2236/// Under the macro-declaration grammar the same invocation keeps the same
2237/// `ERROR` envelope, but the parts inside it are structured further: the macro
2238/// head is a whole `function_declarator` whose parameter list holds the
2239/// arguments, an export-macro word is a `macro_modifier`, a following
2240/// declaration is a `macro_modifier_declarator` or a bare
2241/// `function_declarator` in a sibling `ERROR`, and the hint string explodes
2242/// into word nodes between the `ERROR` nodes that hold its quotes. The reading
2243/// is the same either way: the invocation's own arguments, then one
2244/// declaration at a time until something that is not a declaration stops it.
2245fn macro_wrapped_declarations<'tree>(
2246    envelope: Node<'tree>,
2247    source: &str,
2248    ancestry: &ParentIndex<'tree>,
2249) -> Vec<MacroWrappedDeclaration<'tree>> {
2250    let mut declarations = Vec::new();
2251    if !is_declaration_scope_error(envelope, ancestry) {
2252        return declarations;
2253    }
2254    let mut cursor = envelope.walk();
2255    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2256    let [head, rest @ ..] = children.as_slice() else {
2257        return declarations;
2258    };
2259    let arguments: Vec<Node<'tree>> = if head.kind() == "identifier" {
2260        rest.to_vec()
2261    } else if head.kind() == "function_declarator"
2262        && head
2263            .child_by_field_name("declarator")
2264            .is_some_and(|name| name.kind() == "identifier")
2265    {
2266        let Some(parameters) = head
2267            .child_by_field_name("parameters")
2268            .filter(|parameters| parameters.kind() == "parameter_list")
2269        else {
2270            return declarations;
2271        };
2272        let mut parameters_cursor = parameters.walk();
2273        parameters.named_children(&mut parameters_cursor).collect()
2274    } else {
2275        return declarations;
2276    };
2277    let mut wrapped_declaration_seen = false;
2278    // The hint string's exploded words sit between the `ERROR` nodes holding
2279    // its quotes; inside that bracket they are string content, not
2280    // declaration parts, and they must not start a declaration's range.
2281    let mut in_hint = false;
2282    // Where the specifier words gathered for the declaration whose declarator
2283    // has not arrived yet. Only the macro-declaration grammar's envelopes use
2284    // this: in them an export-macro word is a sibling of the declarator
2285    // rather than a part of its `parameter_declaration`.
2286    let mut pending_start = None;
2287    let mut is_static = false;
2288    for argument in arguments {
2289        if argument.kind() == "comment" {
2290            continue;
2291        }
2292        if argument.kind() == "ERROR"
2293            && (0..argument.child_count())
2294                .filter(|index| {
2295                    argument
2296                        .child(*index)
2297                        .is_some_and(|child| child.kind() == "\"")
2298                })
2299                .count()
2300                % 2
2301                == 1
2302        {
2303            // One of the quote `ERROR`s bracketing the hint string.
2304            in_hint = !in_hint;
2305            pending_start = None;
2306            continue;
2307        }
2308        if in_hint {
2309            continue;
2310        }
2311        match argument.kind() {
2312            "parameter_declaration" => {
2313                let recovered = stranded_declaration_run(argument, source).declarations;
2314                if recovered.is_empty() {
2315                    break;
2316                }
2317                wrapped_declaration_seen = true;
2318                declarations.extend(recovered);
2319            }
2320            // A specifier word of the declaration whose declarator the parser
2321            // handed to a later sibling.
2322            "macro_modifier" if wrapped_declaration_seen => {
2323                pending_start.get_or_insert(argument.start_byte());
2324            }
2325            "storage_class_specifier" if wrapped_declaration_seen => {
2326                pending_start.get_or_insert(argument.start_byte());
2327                is_static |= normalize_cpp_whitespace(node_text(argument, source)) == "static";
2328            }
2329            "macro_modifier_declarator" if wrapped_declaration_seen => {
2330                let Some(declarator) = extract_function_declarator(argument) else {
2331                    break;
2332                };
2333                let start = pending_start
2334                    .take()
2335                    .unwrap_or_else(|| argument.start_byte());
2336                declarations.push(MacroWrappedDeclaration {
2337                    declarator,
2338                    range: cpp_recovery_window(source, start, recovered_declaration_end(argument)),
2339                    is_static,
2340                });
2341                is_static = false;
2342            }
2343            // The hint string, and only that: an argument the recovery cannot
2344            // read as a declaration is admitted before the wrapped declaration
2345            // is found, so a macro whose first argument is not a declaration
2346            // recovers nothing.
2347            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(argument) => {}
2348            // A declaration the parser shredded to a bare function declarator
2349            // and its terminator, its specifier words left as the siblings
2350            // before it.
2351            "ERROR" if wrapped_declaration_seen => {
2352                let mut error_cursor = argument.walk();
2353                let mut parts = argument.named_children(&mut error_cursor);
2354                let Some(declarator) = parts
2355                    .next()
2356                    .filter(|part| part.kind() == "function_declarator")
2357                    .filter(|_| parts.next().is_none())
2358                else {
2359                    break;
2360                };
2361                let start = pending_start
2362                    .take()
2363                    .unwrap_or_else(|| declarator.start_byte());
2364                declarations.push(MacroWrappedDeclaration {
2365                    declarator,
2366                    range: cpp_recovery_window(
2367                        source,
2368                        start,
2369                        recovered_declaration_end(declarator),
2370                    ),
2371                    is_static,
2372                });
2373                is_static = false;
2374            }
2375            _ => break,
2376        }
2377    }
2378    declarations
2379}
2380
2381/// What one macro invocation swallowed when it collapsed a whole run of
2382/// declarations.
2383struct CollapsedMacroDeclarationRun {
2384    /// The byte just past the `;` that closes the invocation, which is where
2385    /// the declarations it swallowed begin.
2386    invocation_end: usize,
2387    /// The byte this recovery owns to: the close of the enclosing declaration
2388    /// scope when the invocation swallowed the declarations written after it,
2389    /// and the invocation's own end when the parser instead spread the
2390    /// invocation itself across the container's children and swallowed nothing.
2391    region_end: usize,
2392}
2393
2394/// The tokens one collapsed macro invocation spans, in source order: the leaves
2395/// of `node`, then the leaves of the siblings written after it. Comments and
2396/// the parser's own MISSING tokens are skipped; neither is in the source the
2397/// invocation is written in.
2398///
2399/// The parser puts the parts of a failed invocation wherever it has room. It
2400/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
2401/// `expression_statement` it recovered with; it spreads them flat over the
2402/// container's children; it packs the tail of the file into one of the
2403/// arguments. The grouping therefore says nothing, but the token order still
2404/// spells the invocation, which is what this reads.
2405struct MacroInvocationTokens<'tree> {
2406    stack: Vec<Node<'tree>>,
2407    /// The last sibling handed to `stack`, and the point the next one is asked
2408    /// for. Held as the node rather than as its already-computed successor so
2409    /// the successor is asked for only when the walk actually runs off the end
2410    /// of what it has: `Node::next_sibling` recovers the parent first, which
2411    /// re-descends from the root, so it costs the node's position in the tree.
2412    /// Nearly every node this iterator is built for is rejected on its first
2413    /// token, before any sibling is needed, and computing one eagerly made that
2414    /// rejection cost a walk of the whole container (capstone's generated
2415    /// instruction tables, t19).
2416    frontier: Option<Node<'tree>>,
2417}
2418
2419impl<'tree> MacroInvocationTokens<'tree> {
2420    fn new(node: Node<'tree>) -> Self {
2421        Self {
2422            stack: vec![node],
2423            frontier: Some(node),
2424        }
2425    }
2426}
2427
2428impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2429    type Item = Node<'tree>;
2430
2431    fn next(&mut self) -> Option<Node<'tree>> {
2432        loop {
2433            let Some(node) = self.stack.pop() else {
2434                let sibling = self.frontier?.next_sibling()?;
2435                self.frontier = Some(sibling);
2436                self.stack.push(sibling);
2437                continue;
2438            };
2439            if node.child_count() == 0 {
2440                if node.kind() == "comment" || node.is_missing() {
2441                    continue;
2442                }
2443                return Some(node);
2444            }
2445            let mut cursor = node.walk();
2446            let children = node.children(&mut cursor).collect::<Vec<_>>();
2447            self.stack.extend(children.into_iter().rev());
2448        }
2449    }
2450}
2451
2452/// The macro invocation that begins at `node` when the parser could not group
2453/// it, or `None` when `node` does not begin one.
2454///
2455/// whisper.cpp's bundled `llama.h` writes
2456///
2457/// ```text
2458/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
2459///                          const char * path_model,
2460///           struct llama_model_params   params),
2461///         "use llama_model_load_from_file instead");
2462/// ```
2463///
2464/// and its own `include/whisper.h` writes
2465///
2466/// ```text
2467/// WHISPER_DEPRECATED(
2468///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
2469///     "use whisper_init_from_file_with_params instead"
2470/// );
2471/// ```
2472///
2473/// tree-sitter cannot know either name is a macro, so it reads the name and its
2474/// `(` as a function declarator whose close it never finds, and carries that
2475/// failure forward over the declarations written after it. In `llama.h` that
2476/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
2477/// (#2551). In `whisper.h` it instead flattens the invocations over the
2478/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
2479/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
2480/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
2481/// `function_definition` when a brace block falls in a swallowed tail, because
2482/// the parser borrows it for the body the bogus definition needs, and an
2483/// `ERROR` or a bare token when none does. None of that says anything about the
2484/// construct, so the node's kind is not the criterion; the token order is.
2485///
2486/// The invocation ends at the `)` that closes its own `(`, immediately followed
2487/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
2488/// balanced by the `(` beside it, and a macro argument list carries no `;` of
2489/// its own, so a `;` reached before the close means this is some other
2490/// construct -- a macro-defined function body, say -- and the ordinary readers
2491/// keep the node.
2492///
2493/// Fails closed everywhere else too. The head must be an export-macro-shaped
2494/// token followed by `(`, the node must stand where declarations live, and an
2495/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
2496/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
2497/// nothing past it -- is left to that reader.
2498fn collapsed_macro_declaration_run<'tree>(
2499    node: Node<'tree>,
2500    source: &str,
2501    ancestry: &ParentIndex<'tree>,
2502) -> Option<CollapsedMacroDeclarationRun> {
2503    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2504    let mut tokens = MacroInvocationTokens::new(node);
2505    let name = tokens.next()?;
2506    if !matches!(name.kind(), "identifier" | "type_identifier")
2507        || !cpp_export_macro_token(node_text(name, source))
2508    {
2509        return None;
2510    }
2511    if tokens.next()?.kind() != "(" {
2512        return None;
2513    }
2514    let mut depth = 1usize;
2515    let invocation_end = loop {
2516        let token = tokens.next()?;
2517        match token.kind() {
2518            "(" => depth += 1,
2519            ";" => return None,
2520            ")" => {
2521                depth -= 1;
2522                if depth == 0 {
2523                    let semicolon = tokens.next()?;
2524                    if semicolon.kind() != ";" {
2525                        return None;
2526                    }
2527                    break semicolon.end_byte();
2528                }
2529            }
2530            _ => {}
2531        }
2532    };
2533    // An invocation that reaches the end of its own declaration scope swallowed
2534    // nothing: there is nothing after it left to recover, and the ordinary
2535    // readers have the invocation itself. This is also what stops the scan
2536    // below from re-entering itself, since the region it reparses for one
2537    // invocation ends exactly at that invocation's `;`.
2538    if invocation_end >= container_end {
2539        return None;
2540    }
2541    // Where the damage ends. A node that reaches past its own invocation packed
2542    // real declarations into the invocation's argument list, so the parser was
2543    // still failing when it got there and everything to the close of the scope
2544    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2545    // line 455 with it, and every declaration from there to the end of the file
2546    // is shredded. A node that stops at or before the invocation's `;` holds
2547    // nothing but the invocation, whose own parts the parser spread over the
2548    // siblings after it.
2549    let region_end = if node.end_byte() > invocation_end {
2550        container_end
2551    } else {
2552        invocation_end
2553    };
2554    Some(CollapsedMacroDeclarationRun {
2555        invocation_end,
2556        region_end,
2557    })
2558}
2559
2560/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2561/// ordinary class body, and the members it swallowed.
2562///
2563/// Botan deprecates members that way:
2564///
2565/// ```text
2566/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2567/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2568/// ```
2569///
2570/// The parser reads the macro name as the member's type and its argument list
2571/// as a parenthesized declarator, which then swallows the attributed member
2572/// *and* the member written after it: a `field_declaration` whose `type` is a
2573/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2574/// opening with an `ERROR` whose first part is a bare identifier -- the first
2575/// word of the string, which the lexer could not keep together.
2576///
2577/// The macro's spelling is not the criterion; that structural shape is. The
2578/// returned declarations are in source order, the first being the attributed
2579/// member and the rest the members the declarator swallowed after it.
2580fn string_attribute_macro_member_declarators<'tree>(
2581    field: Node<'tree>,
2582    source: &str,
2583) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2584    if field.kind() != "field_declaration"
2585        || field
2586            .child_by_field_name("type")
2587            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2588    {
2589        return None;
2590    }
2591    let declarator = field.child_by_field_name("declarator")?;
2592    if declarator.kind() != "parenthesized_declarator" {
2593        return None;
2594    }
2595    let opening = declarator.named_child(0)?;
2596    if opening.kind() != "ERROR"
2597        || opening
2598            .named_child(0)
2599            .is_none_or(|word| word.kind() != "identifier")
2600    {
2601        return None;
2602    }
2603    let declarations = stranded_declaration_run(declarator, source).declarations;
2604    (!declarations.is_empty()).then_some(declarations)
2605}
2606
2607/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2608/// export-macro class body leaves in front of the members that macro decorated.
2609///
2610/// The reparse reads the class body as statements, so the attribute becomes a
2611/// call statement of its own -- with the `;` the grammar had to invent -- and
2612/// the members after it are stranded in the `ERROR` that follows.
2613fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2614    let Some(call) = matches!(node.kind(), "expression_statement" | "ERROR")
2615        .then(|| node.named_child(0))
2616        .flatten()
2617        .filter(|child| child.kind() == "call_expression")
2618    else {
2619        return false;
2620    };
2621    call.child_by_field_name("function")
2622        .is_some_and(|function| function.kind() == "identifier")
2623        && call
2624            .child_by_field_name("arguments")
2625            .is_some_and(|arguments| {
2626                let mut cursor = arguments.walk();
2627                arguments.named_child_count() > 0
2628                    && arguments
2629                        .named_children(&mut cursor)
2630                        .all(|argument| argument.kind() == "string_literal")
2631            })
2632}
2633
2634/// The start byte of the call the region reparse left where an access-labeled
2635/// constructor was written.
2636///
2637/// A constructor is a member only inside a class body. The export-macro class
2638/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2639/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2640/// comma-expression under `private:`, with no declarator left in the tree. It
2641/// is still spelled in the source, though, starting at the one call under the
2642/// label whose callee is the class's own name. Botan's private six-parameter
2643/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2644/// call is an ambiguity this declines, and so is a reparse from that byte that
2645/// does not yield exactly one constructor declarator there.
2646fn cpp_access_label_constructor_call_start(
2647    node: Node<'_>,
2648    class_name: &str,
2649    source: &str,
2650) -> Option<usize> {
2651    if node.kind() != "labeled_statement" {
2652        return None;
2653    }
2654    let label = node.named_child(0)?;
2655    if label.kind() != "statement_identifier"
2656        || !matches!(
2657            node_text(label, source).trim(),
2658            "public" | "private" | "protected"
2659        )
2660    {
2661        return None;
2662    }
2663    let mut starts = Vec::new();
2664    let mut stack = vec![node];
2665    while let Some(current) = stack.pop() {
2666        if current.kind() == "call_expression"
2667            && current
2668                .child_by_field_name("function")
2669                .is_some_and(|function| {
2670                    function.kind() == "identifier"
2671                        && node_text(function, source).trim() == class_name
2672                })
2673        {
2674            starts.push(current.start_byte());
2675        }
2676        // The macro-declaration grammar reads the constructor head as a
2677        // declaration whose type is the class name and whose declarators are
2678        // the shredded parameter list and initializers; the head's start is
2679        // the type's start.
2680        if current.kind() == "declaration"
2681            && current
2682                .child_by_field_name("type")
2683                .is_some_and(|type_node| {
2684                    type_node.kind() == "type_identifier"
2685                        && node_text(type_node, source).trim() == class_name
2686                })
2687        {
2688            starts.push(
2689                current
2690                    .child_by_field_name("type")
2691                    .expect("checked declaration type")
2692                    .start_byte(),
2693            );
2694        }
2695        let mut cursor = current.walk();
2696        stack.extend(current.named_children(&mut cursor));
2697    }
2698    let [start] = starts.as_slice() else {
2699        return None;
2700    };
2701    Some(*start)
2702}
2703
2704/// The `function_definition` that gives `declarator` a body, following only the
2705/// declarator chain, so a recovered callable knows whether it is a declaration
2706/// or a definition without anyone having to say.
2707fn cpp_declarator_function_definition<'tree>(
2708    declarator: Node<'tree>,
2709    ancestry: &ParentIndex<'tree>,
2710) -> Option<Node<'tree>> {
2711    let mut current = declarator;
2712    while let Some(parent) = ancestry.parent(current) {
2713        match parent.kind() {
2714            "function_definition" if parent.child_by_field_name("body").is_some() => {
2715                return Some(parent);
2716            }
2717            "pointer_declarator"
2718            | "reference_declarator"
2719            | "parenthesized_declarator"
2720            | "array_declarator" => current = parent,
2721            _ => return None,
2722        }
2723    }
2724    None
2725}
2726
2727/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2728/// declaration walk still reaches, so a recovery that runs ahead of that walk
2729/// would name what it finds without the namespace.
2730fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2731    let mut current = node;
2732    while let Some(parent) = ancestry.parent(current) {
2733        if parent.kind() == "namespace_definition"
2734            && parent.child_by_field_name("body") == Some(current)
2735        {
2736            return true;
2737        }
2738        current = parent;
2739    }
2740    false
2741}
2742
2743/// Whether the source a recovered callable's byte range spells is a definition
2744/// (a declarator with a body) rather than a declaration.
2745///
2746/// A callable recovered from a mangled region owns no node of its own in the
2747/// file's tree, so the occurrence-role scan that climbs from the range to the
2748/// node containing it lands on whatever container the parser left and answers
2749/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2750/// constructor a declaration and left its call site with nothing to navigate to
2751/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2752/// extraction used, and read the answer from the one declaration it spells.
2753///
2754/// `None` when the range is not one recovered declaration on its own, which is
2755/// the case for every ordinary declaration, so callers keep their own answer.
2756pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2757    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2758    let root = tree.root_node();
2759    let mut cursor = root.walk();
2760    let items = root
2761        .named_children(&mut cursor)
2762        .filter(|child| child.kind() != "comment")
2763        .collect::<Vec<_>>();
2764    let [item] = items.as_slice() else {
2765        return None;
2766    };
2767    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2768        return None;
2769    }
2770    match item.kind() {
2771        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2772        "declaration" | "field_declaration" => Some(false),
2773        _ => None,
2774    }
2775}
2776
2777/// Whether `node` is an envelope an attribute-like macro invocation left where
2778/// declarations were written: the declaration-scope `ERROR`
2779/// [`macro_wrapped_declarations`] reads, or the collapsed run
2780/// [`collapsed_macro_declaration_run`] reads.
2781///
2782/// Extraction and resolution must read one definition of this shape. The
2783/// resolver climbs from a declaration's recorded byte range to the node that
2784/// declares it, and a recovered declaration's range lies inside one of these
2785/// envelopes rather than inside a `declaration` node, so the climb stops here
2786/// (the same role `is_recovered_exported_class_container` plays for a recovered
2787/// class).
2788pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2789    let ancestry = ParentIndex::unindexed();
2790    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2791        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2792}
2793
2794fn recover_exported_class_function_definition<'tree>(
2795    node: Node<'tree>,
2796    source: &str,
2797) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2798    if node.kind() != "function_definition" {
2799        return None;
2800    }
2801    if let Some(prefix) = node.prev_named_sibling()
2802        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2803        && recovered.range.end_byte == node.end_byte()
2804    {
2805        return Some((
2806            node,
2807            recovered.name,
2808            recovered.raw_supertypes,
2809            recovered.base_facts,
2810        ));
2811    }
2812    let type_node = node.child_by_field_name("type")?;
2813    let declarator = node.child_by_field_name("declarator")?;
2814
2815    if matches!(
2816        type_node.kind(),
2817        "class_specifier" | "struct_specifier" | "union_specifier"
2818    ) {
2819        let type_name = type_node
2820            .child_by_field_name("name")
2821            .and_then(|name| direct_identifier_name(name, source));
2822        let exported_macro_type = type_name
2823            .as_ref()
2824            .is_some_and(|name| cpp_export_macro_token(name));
2825        if exported_macro_type {
2826            let mut cursor = node.walk();
2827            let errors_before_declarator = node
2828                .named_children(&mut cursor)
2829                .filter(|child| {
2830                    child.kind() == "ERROR"
2831                        && child.start_byte() >= type_node.end_byte()
2832                        && child.end_byte() <= declarator.start_byte()
2833                })
2834                .collect::<Vec<_>>();
2835            if let Some(name) = errors_before_declarator
2836                .iter()
2837                .find_map(|error| displaced_exported_class_name(*error, source))
2838            {
2839                let base_node = errors_before_declarator
2840                    .iter()
2841                    .any(|error| malformed_inheritance_syntax(*error))
2842                    .then_some(declarator);
2843                let raw_supertypes = base_node
2844                    .and_then(|base| recovered_malformed_base_name(base, source))
2845                    .map(|base| vec![base]);
2846                let base_facts = base_node
2847                    .and_then(|base| {
2848                        recovered_base_specifier(
2849                            base,
2850                            source,
2851                            recovered_base_is_virtual(node, base, source),
2852                        )
2853                    })
2854                    .map(|base| vec![base]);
2855                return Some((node, name, raw_supertypes, base_facts));
2856            }
2857            if errors_before_declarator
2858                .iter()
2859                .any(|error| malformed_inheritance_syntax(*error))
2860            {
2861                return None;
2862            }
2863        }
2864        if !exported_macro_type
2865            && let Some(name) = type_name
2866            && !cpp_export_macro_token(&name)
2867            && let Some(base) =
2868                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2869        {
2870            let base_node =
2871                recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2872            let base_facts = Some(vec![recovered_base_specifier(
2873                base_node,
2874                source,
2875                recovered_base_is_virtual(node, base_node, source),
2876            )?]);
2877            return Some((node, name, Some(vec![base]), base_facts));
2878        }
2879        // The macro-declaration grammar reads the displaced class name and the
2880        // `final` that follows it as a `macro_modifier_declarator`; read the
2881        // name positionally from the head's tokens, the same rule the other
2882        // recovered export heads use (#2557).
2883        let declarator_name = direct_identifier_name(declarator, source).or_else(|| {
2884            (declarator.kind() == "macro_modifier_declarator")
2885                .then(|| node.child_by_field_name("body"))
2886                .flatten()
2887                .filter(|body| body.kind() == "compound_statement")
2888                .and_then(|body| recovered_export_head_name(node, body, source))
2889                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
2890        });
2891        if let Some(name) = declarator_name
2892            && exported_macro_type
2893            && !cpp_export_macro_token(&name)
2894        {
2895            let base_node = exported_macro_type
2896                .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2897                .flatten();
2898            let raw_supertypes = base_node
2899                .and_then(|base| recovered_malformed_base_name(base, source))
2900                .map(|base| vec![base]);
2901            let base_facts = base_node
2902                .and_then(|base| {
2903                    recovered_base_specifier(
2904                        base,
2905                        source,
2906                        recovered_base_is_virtual(node, base, source),
2907                    )
2908                })
2909                .map(|base| vec![base]);
2910            return Some((node, name, raw_supertypes, base_facts));
2911        }
2912        if declarator.kind() == "parenthesized_declarator"
2913            && type_node
2914                .child_by_field_name("name")
2915                .and_then(|name| direct_identifier_name(name, source))
2916                .is_some_and(|name| cpp_export_macro_token(&name))
2917        {
2918            if let Some((name, base_node)) =
2919                recovered_function_like_export_class_owner_node(declarator, source)
2920            {
2921                return Some((
2922                    node,
2923                    name,
2924                    Some(vec![recovered_malformed_base_name(base_node, source)?]),
2925                    Some(vec![recovered_base_specifier(
2926                        base_node,
2927                        source,
2928                        recovered_base_is_virtual(node, base_node, source),
2929                    )?]),
2930                ));
2931            }
2932            let body_start = node
2933                .child_by_field_name("body")
2934                .map(|body| body.start_byte())
2935                .unwrap_or(node.end_byte());
2936            let mut cursor = node.walk();
2937            if let Some(name) = node
2938                .named_children(&mut cursor)
2939                .filter(|child| {
2940                    child.kind() == "ERROR"
2941                        && child.start_byte() >= declarator.end_byte()
2942                        && child.end_byte() <= body_start
2943                })
2944                .find_map(|error| declarator_name_from_node(error, source))
2945            {
2946                return Some((node, name, None, None));
2947            }
2948            // The macro-declaration grammar keeps a head without a base clause
2949            // whole inside the declarator: `( ERROR(2, 2)) TOTP final` with a
2950            // missing `)`, the name and `final` wrapped as a
2951            // `macro_modifier_declarator` or the name left bare. Read the name
2952            // positionally from the declarator's own tokens (#2557). The macro
2953            // is the `type` field, so a capitalized name (`TOTP`) is the class.
2954            if let Some(body) = node
2955                .child_by_field_name("body")
2956                .filter(|body| body.kind() == "compound_statement")
2957                && let Some(name_node) =
2958                    export_head_name_from_tokens(&export_head_tokens(declarator, None), source)
2959            {
2960                let name = normalize_cpp_whitespace(node_text(name_node, source));
2961                if !name.is_empty() {
2962                    let (bases, base_facts) = recovered_export_head_base_data(
2963                        declarator,
2964                        name_node.end_byte(),
2965                        body.start_byte(),
2966                        source,
2967                    );
2968                    return Some((
2969                        node,
2970                        name,
2971                        (!bases.is_empty()).then_some(bases),
2972                        (!base_facts.is_empty()).then_some(base_facts),
2973                    ));
2974                }
2975            }
2976        }
2977    }
2978
2979    // The macro-declaration grammar reads a namespace-opening macro before an
2980    // export-macro class head (`NS_ENTER(ns) class API Service { ... }`) with
2981    // the invocation as the `macro_type_specifier` type and the class head as
2982    // a `macro_modifier_declarator` chain. The class name is the last head
2983    // token before the body, per the positional rule (#2557).
2984    if type_node.kind() == "macro_type_specifier"
2985        && declarator.kind() == "macro_modifier_declarator"
2986        && type_node
2987            .child_by_field_name("name")
2988            .and_then(|name| direct_identifier_name(name, source))
2989            .is_some_and(|name| cpp_export_macro_token(&name))
2990    {
2991        let body = node
2992            .child_by_field_name("body")
2993            .filter(|body| body.kind() == "compound_statement")?;
2994        let tokens = export_head_tokens(node, Some(body));
2995        let has_class_keyword = tokens.iter().any(|token| {
2996            matches!(token.kind(), "identifier" | "type_identifier")
2997                && matches!(node_text(*token, source), "class" | "struct" | "union")
2998        });
2999        if !has_class_keyword {
3000            return None;
3001        }
3002        let name_node = recovered_export_head_name(node, body, source)?;
3003        let name = normalize_cpp_whitespace(node_text(name_node, source));
3004        if name.is_empty() || cpp_export_macro_token(&name) {
3005            return None;
3006        }
3007        return Some((node, name, None, None));
3008    }
3009
3010    let declarator_text = direct_identifier_name(declarator, source)?;
3011    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
3012        return None;
3013    }
3014    class_identifier_before_body(node, source).map(|name| (node, name, None, None))
3015}
3016
3017fn recovered_function_like_export_class_owner_node<'tree>(
3018    declarator: Node<'tree>,
3019    source: &str,
3020) -> Option<(String, Node<'tree>)> {
3021    if declarator.kind() != "parenthesized_declarator" {
3022        return None;
3023    }
3024    let mut cursor = declarator.walk();
3025    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
3026    let [prefix, base] = children.as_slice() else {
3027        return None;
3028    };
3029    if prefix.kind() != "ERROR"
3030        || !matches!(
3031            base.kind(),
3032            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
3033        )
3034    {
3035        return None;
3036    }
3037    let mut identifiers = Vec::new();
3038    let mut prefix_cursor = prefix.walk();
3039    let mut parts = prefix
3040        .named_children(&mut prefix_cursor)
3041        .collect::<Vec<_>>();
3042    // The macro-declaration grammar wraps the displaced name and its `final`
3043    // as a `macro_modifier_declarator`; read the identifiers it wraps the same
3044    // way as the bare ones the older grammar left here.
3045    while let Some(child) = parts.pop() {
3046        match child.kind() {
3047            "number_literal" | "string_literal" | "char_literal" => {}
3048            "identifier" | "type_identifier" => {
3049                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3050            }
3051            "macro_modifier" => {
3052                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3053            }
3054            "macro_modifier_declarator" => {
3055                parts.extend(child.named_children(&mut child.walk()));
3056            }
3057            _ => return None,
3058        }
3059    }
3060    identifiers.reverse();
3061    let name = match identifiers.as_slice() {
3062        [name] => name.clone(),
3063        [name, final_token] if final_token == "final" => name.clone(),
3064        _ => return None,
3065    };
3066    if name.is_empty() || cpp_export_macro_token(&name) {
3067        return None;
3068    }
3069    Some((name, *base))
3070}
3071
3072/// Collect the base names and source facts tree-sitter scattered across the
3073/// recovered head of a function-like export-macro class. A base arrives either
3074/// as a direct sibling identifier or inside the `ERROR` node the grammar
3075/// produced for a `: public Base` fragment. The grammar leaves the `final`
3076/// specifier and access specifiers in the same position as the bases, so drop
3077/// them.
3078/// The bases a recovered function-like export-macro class head names between
3079/// `after` (the end of the class name, or of the access specifier that stands
3080/// in for it) and `before` (the start of the body, or of the `init_declarator`
3081/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
3082/// fragments, and one fragment can also hold the class name and `final`
3083/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
3084/// than by the fragment that holds it.
3085fn recovered_export_head_base_data(
3086    node: Node<'_>,
3087    after: usize,
3088    before: usize,
3089    source: &str,
3090) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
3091    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
3092    let mut base_nodes = Vec::new();
3093    let mut cursor = node.walk();
3094    for child in node.named_children(&mut cursor) {
3095        if child.kind() == "ERROR" {
3096            let mut error_cursor = child.walk();
3097            base_nodes.extend(
3098                child
3099                    .named_children(&mut error_cursor)
3100                    .filter(within)
3101                    .filter_map(|part| {
3102                        recovered_malformed_base_name(part, source).map(|_| (part, child))
3103                    }),
3104            );
3105        } else if child.kind() == "macro_modifier_declarator" {
3106            // The modifier words are access specifiers or decoration; the base
3107            // is the wrapper's terminal declarator.
3108            let mut current = child;
3109            while let Some(inner) = current.child_by_field_name("declarator") {
3110                if inner.kind() == "macro_modifier_declarator" {
3111                    current = inner;
3112                    continue;
3113                }
3114                if within(&inner) && recovered_malformed_base_name(inner, source).is_some() {
3115                    base_nodes.push((inner, child));
3116                }
3117                break;
3118            }
3119        } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
3120            base_nodes.push((child, node));
3121        }
3122    }
3123    let mut bases = Vec::new();
3124    let mut facts = Vec::new();
3125    for (base, container) in base_nodes {
3126        let Some(name) = recovered_malformed_base_name(base, source) else {
3127            continue;
3128        };
3129        if matches!(
3130            name.as_str(),
3131            "final" | "public" | "protected" | "private" | "virtual"
3132        ) {
3133            continue;
3134        }
3135        bases.push(name);
3136        if let Some(fact) = recovered_base_specifier(
3137            base,
3138            source,
3139            recovered_base_is_virtual(container, base, source),
3140        ) {
3141            facts.push(fact);
3142        }
3143    }
3144    (bases, facts)
3145}
3146
3147fn recovered_export_head_bases(
3148    node: Node<'_>,
3149    after: usize,
3150    before: usize,
3151    source: &str,
3152) -> Vec<String> {
3153    recovered_export_head_base_data(node, after, before, source).0
3154}
3155
3156/// Whether `token` is the `final` that closes a recovered class head. The
3157/// grammar keeps it as the `final` keyword when it recognised the head
3158/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
3159/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
3160fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
3161    if token.is_named() {
3162        token.kind() == "identifier" && node_text(token, source) == "final"
3163    } else {
3164        token.kind() == "final"
3165    }
3166}
3167
3168/// The class name of a recovered function-like export-macro class head, read by
3169/// position rather than spelling (#2557). `node` is the sibling tree-sitter
3170/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
3171/// carries the body. The head's tokens, in source order with `ERROR` fragments
3172/// flattened, read `macros... name [final] [: bases...]`: the name is the last
3173/// identifier before the head ends, and the head ends at `final`, at the base
3174/// clause `:`, or at `tail`. Every identifier before the name is decoration
3175/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
3176/// (`X509_CA`) is a class name like any other.
3177///
3178/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
3179/// rule, not a token of the source, so it is never the name.
3180fn recovered_export_head_name<'tree>(
3181    node: Node<'tree>,
3182    tail: Node<'tree>,
3183    source: &str,
3184) -> Option<Node<'tree>> {
3185    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
3186}
3187
3188/// The class name of a head whose tokens straddle the recovered pair: the
3189/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
3190/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
3191/// The positional rule is the same, read over the two token runs in source
3192/// order, so a name in either node wins by position rather than by which node
3193/// holds it.
3194fn recovered_export_pair_head_name<'tree>(
3195    prefix: Node<'tree>,
3196    sibling: Node<'tree>,
3197    tail: Node<'tree>,
3198    source: &str,
3199) -> Option<Node<'tree>> {
3200    let mut tokens = export_head_tokens(prefix, None);
3201    tokens.extend(export_head_tokens(sibling, Some(tail)));
3202    export_head_name_from_tokens(&tokens, source)
3203}
3204
3205/// One node's head tokens in source order, with `ERROR` fragments spliced in
3206/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
3207/// before `tail` when the caller names one. A `macro_modifier` or
3208/// `macro_modifier_declarator` is the macro-declaration grammar's structured
3209/// form of the same grouping failure, so its children join the run in place
3210/// the same way, recursively.
3211fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
3212    let mut tokens = Vec::new();
3213    let mut stack = Vec::new();
3214    let mut cursor = node.walk();
3215    stack.extend(
3216        node.children(&mut cursor)
3217            .collect::<Vec<_>>()
3218            .into_iter()
3219            .rev(),
3220    );
3221    while let Some(child) = stack.pop() {
3222        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
3223            break;
3224        }
3225        if matches!(
3226            child.kind(),
3227            "ERROR" | "macro_modifier" | "macro_modifier_declarator"
3228        ) {
3229            let mut fragment_cursor = child.walk();
3230            stack.extend(
3231                child
3232                    .children(&mut fragment_cursor)
3233                    .collect::<Vec<_>>()
3234                    .into_iter()
3235                    .rev(),
3236            );
3237        } else {
3238            tokens.push(child);
3239        }
3240    }
3241    tokens
3242}
3243
3244/// The last identifier before the head ends, per the positional rule.
3245fn export_head_name_from_tokens<'tree>(
3246    tokens: &[Node<'tree>],
3247    source: &str,
3248) -> Option<Node<'tree>> {
3249    let end = tokens
3250        .iter()
3251        .position(|token| {
3252            recovered_export_head_final(*token, source)
3253                || (!token.is_named() && token.kind() == ":")
3254        })
3255        .unwrap_or(tokens.len());
3256    export_head_last_identifier(tokens[..end].iter().copied())
3257}
3258
3259fn export_head_last_identifier<'tree>(
3260    candidates: impl IntoIterator<Item = Node<'tree>>,
3261) -> Option<Node<'tree>> {
3262    candidates
3263        .into_iter()
3264        .filter(|candidate| {
3265            candidate.is_named()
3266                && !candidate.is_missing()
3267                && matches!(
3268                    candidate.kind(),
3269                    "identifier" | "type_identifier" | "field_identifier"
3270                )
3271        })
3272        .last()
3273}
3274
3275/// The `init_declarator` whose `value` carries the class body when the grammar
3276/// keeps a function-like export-macro class head as one `declaration`.
3277fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
3278    let mut cursor = declaration.walk();
3279    declaration
3280        .named_children(&mut cursor)
3281        .find(|child| child.kind() == "init_declarator")
3282}
3283
3284/// Read the bases and the initializer-list body of a `declaration`-shaped tail
3285/// of a function-like export-macro class head. The grammar splits a base list
3286/// across bare declarator fields, `ERROR` fragments, and one trailing
3287/// `init_declarator` whose `value` is the class body. `head_end` is where the
3288/// class identity ends and the bases can begin: the end of the access specifier
3289/// when the class name went to a statement label, and the end of the class name
3290/// itself otherwise.
3291fn recovered_export_declaration_tail<'tree>(
3292    declaration: Node<'tree>,
3293    head_end: usize,
3294    source: &str,
3295) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
3296    let init = recovered_export_init_declarator(declaration)?;
3297    let body = init.child_by_field_name("value")?;
3298    if body.kind() != "initializer_list" {
3299        return None;
3300    }
3301    let (mut bases, mut facts) =
3302        recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
3303    let (more_bases, more_facts) =
3304        recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
3305    bases.extend(more_bases);
3306    facts.extend(more_facts);
3307    Some((bases, (!facts.is_empty()).then_some(facts), body))
3308}
3309
3310/// Whether `node` is the head fragment tree-sitter leaves where
3311/// `class MACRO(args)` was written: the class keyword and the export macro's
3312/// function-like invocation in that order, with nothing else between them.
3313/// Spelling plays no part -- the invocation's `(` following the macro
3314/// identifier directly is what makes it function-like (#2557).
3315///
3316/// Two reductions produce the head. On its own the grammar keeps a body-less
3317/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
3318/// with the invocation's `(`, arguments and `)` beside it. After object-like
3319/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
3320/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
3321/// `declaration` whose class keyword is demoted to a bare `identifier` and
3322/// whose macro invocation becomes the trailing `init_declarator` (#2924).
3323fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
3324    match node.kind() {
3325        "ERROR" => {
3326            let Some(class_node) = first_class_like_child(node) else {
3327                return false;
3328            };
3329            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
3330                return false;
3331            }
3332            if class_node
3333                .child_by_field_name("name")
3334                .and_then(|name| direct_identifier_name(name, source))
3335                .is_none()
3336            {
3337                return false;
3338            }
3339            class_node
3340                .next_sibling()
3341                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
3342        }
3343        "declaration" => {
3344            let mut cursor = node.walk();
3345            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3346            // The class keyword lost its own node: it survives as a bare
3347            // identifier spelled `class`, `struct` or `union`, which is never a
3348            // declarator or a type in well-formed code. It can sit inside an
3349            // `ERROR` beside the object-like macro names, and an `ERROR` here is
3350            // a grouping failure rather than a construct, so read through it.
3351            let Some(keyword) = children
3352                .iter()
3353                .copied()
3354                .flat_map(|child| {
3355                    let mut cursor = child.walk();
3356                    if child.kind() == "ERROR" {
3357                        child.named_children(&mut cursor).collect::<Vec<_>>()
3358                    } else {
3359                        vec![child]
3360                    }
3361                })
3362                .find(|child| {
3363                    child.kind() == "identifier"
3364                        && matches!(node_text(*child, source), "class" | "struct" | "union")
3365                })
3366            else {
3367                return false;
3368            };
3369            children.last().is_some_and(|init| {
3370                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
3371                    let mut cursor = init.walk();
3372                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
3373                    matches!(parts.as_slice(), [macro_name, arguments]
3374                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
3375                            && arguments.kind() == "argument_list")
3376                }
3377            })
3378        }
3379        _ => false,
3380    }
3381}
3382
3383fn recover_function_like_export_class_pair(
3384    node: Node<'_>,
3385    source: &str,
3386) -> Option<RecoveredFunctionLikeExportClassPair> {
3387    if !is_function_like_export_class_head(node, source) {
3388        return None;
3389    }
3390    let sibling = node.next_named_sibling()?;
3391    let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
3392        // At translation-unit scope tree-sitter can leave the malformed class
3393        // head in one ERROR node and parse its body as the adjacent compound
3394        // statement. The head still carries the export invocation, displaced
3395        // class name, and optional `final` token in source order, so recover
3396        // the name by the same positional rule as the other shapes.
3397        "compound_statement" => (
3398            recovered_export_pair_head_name(node, sibling, sibling, source)
3399                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
3400            None,
3401            None,
3402            sibling,
3403        ),
3404        "expression_statement" => {
3405            let compound = sibling.named_child(0)?;
3406            if compound.kind() != "compound_literal_expression" {
3407                return None;
3408            }
3409            let body = compound.child_by_field_name("value")?;
3410            if body.kind() != "initializer_list" {
3411                return None;
3412            }
3413            (
3414                compound
3415                    .child_by_field_name("type")
3416                    .and_then(|name| direct_identifier_name(name, source))?,
3417                None,
3418                None,
3419                body,
3420            )
3421        }
3422        "labeled_statement" => {
3423            let label = sibling.child_by_field_name("label")?;
3424            if label.kind() != "statement_identifier" {
3425                return None;
3426            }
3427            let name = normalize_cpp_whitespace(node_text(label, source));
3428            let declaration = sibling
3429                .named_children(&mut sibling.walk())
3430                .find(|child| child.kind() == "declaration")?;
3431            let access = declaration.child_by_field_name("type")?;
3432            if !matches!(
3433                node_text(access, source),
3434                "public" | "protected" | "private"
3435            ) {
3436                return None;
3437            }
3438            let (bases, base_facts, body) =
3439                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3440            (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3441        }
3442        // `class MACRO(2, 0) Name final { ... };`,
3443        // `class MACRO(2, 0) Name final : public Base { ... };`, and
3444        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
3445        // spread over the `type` field, the declarator field, and `ERROR`
3446        // fragments beside them; the name is the last one before the head ends.
3447        "function_definition" => {
3448            let body = sibling.child_by_field_name("body")?;
3449            if body.kind() != "compound_statement" {
3450                return None;
3451            }
3452            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3453            let (bases, base_facts) = recovered_export_head_base_data(
3454                sibling,
3455                name_node.end_byte(),
3456                body.start_byte(),
3457                source,
3458            );
3459            (
3460                normalize_cpp_whitespace(node_text(name_node, source)),
3461                (!bases.is_empty()).then_some(bases),
3462                (!base_facts.is_empty()).then_some(base_facts),
3463                body,
3464            )
3465        }
3466        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
3467        // comma-separated base list makes the grammar keep the whole tail as one
3468        // declaration whose trailing `init_declarator` carries the body.
3469        "declaration" => {
3470            let init = recovered_export_init_declarator(sibling)?;
3471            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3472            let (bases, base_facts, body) =
3473                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3474            (
3475                normalize_cpp_whitespace(node_text(name_node, source)),
3476                (!bases.is_empty()).then_some(bases),
3477                base_facts,
3478                body,
3479            )
3480        }
3481        _ => return None,
3482    };
3483    debug_assert!(
3484        !name.is_empty(),
3485        "a recovered class head names its class by an identifier token"
3486    );
3487    let range = Range {
3488        start_byte: node.start_byte(),
3489        end_byte: sibling.end_byte(),
3490        start_line: node.start_position().row + 1,
3491        end_line: sibling.end_position().row + 1,
3492    };
3493    Some(RecoveredFunctionLikeExportClassPair {
3494        name,
3495        raw_supertypes,
3496        base_facts,
3497        range,
3498        fragmented_body: recovered_fragmented_export_body(body, range)?,
3499    })
3500}
3501
3502/// Recover a function-like export-macro class that tree-sitter embedded in a
3503/// larger error after an earlier malformed class body. The grammar still
3504/// preserves every part of the class head: the `class` token, export macro
3505/// identifier and argument list, displaced class identifier, access specifier,
3506/// base field, and initializer-list-shaped body. Match only that complete
3507/// structured sequence and keep each recovered class's exact byte envelope.
3508fn recover_embedded_function_like_export_classes(
3509    node: Node<'_>,
3510    source: &str,
3511) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3512    if !node.is_error() {
3513        return Vec::new();
3514    }
3515
3516    let mut nodes = Vec::new();
3517    let mut class_tokens = Vec::new();
3518    let mut stack = vec![node];
3519    while let Some(current) = stack.pop() {
3520        nodes.push(current);
3521        // An error subtree without an anonymous class token cannot contain
3522        // a recoverable class head. Detect that before sorting or resolving
3523        // kind-id tables; the grammar spelling matches all aliased symbol ids.
3524        if !current.is_named() && current.kind() == "class" {
3525            class_tokens.push(current);
3526        }
3527        push_children_reversed(current, &mut stack);
3528    }
3529    if class_tokens.is_empty() {
3530        return Vec::new();
3531    }
3532    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3533    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3534
3535    // The scans below ask the same handful of kind questions once per node of
3536    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
3537    let language = node.language();
3538    let class_kind = NodeKindIds::new(&language, "class");
3539    let identifier_kinds = [
3540        NodeKindIds::new(&language, "identifier"),
3541        NodeKindIds::new(&language, "type_identifier"),
3542        NodeKindIds::new(&language, "field_identifier"),
3543    ];
3544    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3545    let colon_kind = NodeKindIds::new(&language, ":");
3546    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3547
3548    let mut recovered = Vec::new();
3549    for class_token in class_tokens {
3550        debug_assert!(class_kind.matches(class_token));
3551        let row = class_token.start_position().row;
3552        // The head after the `class` token reads `MACRO(args) macros... name
3553        // [final] : bases`. The macro is the first identifier, a function-like
3554        // invocation when its argument list is the next thing after it, and the
3555        // name is the last identifier before the head ends at `final` or at the
3556        // base clause `:`. Spelling plays no part (#2557).
3557        let is_identifier = |candidate: &Node<'_>| {
3558            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3559        };
3560        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3561            candidate.start_byte() >= class_token.end_byte()
3562                && candidate.start_position().row == row
3563                && is_identifier(candidate)
3564        }) else {
3565            continue;
3566        };
3567        let Some(arguments) = nodes.iter().copied().find(|candidate| {
3568            argument_list_kind.matches(*candidate)
3569                && candidate.start_byte() >= macro_name.end_byte()
3570                && candidate.start_position().row == row
3571        }) else {
3572            continue;
3573        };
3574        if nodes.iter().any(|candidate| {
3575            is_identifier(candidate)
3576                && candidate.start_byte() >= macro_name.end_byte()
3577                && candidate.end_byte() <= arguments.start_byte()
3578        }) {
3579            continue;
3580        }
3581        let Some(head_end) = nodes.iter().copied().find(|candidate| {
3582            candidate.start_byte() >= arguments.end_byte()
3583                && (recovered_export_head_final(*candidate, source)
3584                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
3585        }) else {
3586            continue;
3587        };
3588        let Some(name_node) =
3589            export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3590                is_identifier(candidate)
3591                    && candidate.start_byte() >= arguments.end_byte()
3592                    && candidate.end_byte() <= head_end.start_byte()
3593                    && candidate.start_position().row == row
3594            }))
3595        else {
3596            continue;
3597        };
3598        let name = normalize_cpp_whitespace(node_text(name_node, source));
3599        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3600            field_initializer_kind.matches(*candidate)
3601                && candidate.start_byte() >= name_node.end_byte()
3602                && candidate
3603                    .child_by_field_name("field")
3604                    .or_else(|| candidate.named_child(0))
3605                    .is_some()
3606                && candidate
3607                    .child_by_field_name("value")
3608                    .or_else(|| {
3609                        let mut cursor = candidate.walk();
3610                        candidate
3611                            .named_children(&mut cursor)
3612                            .find(|child| child.kind() == "initializer_list")
3613                    })
3614                    .is_some_and(|value| value.kind() == "initializer_list")
3615        }) else {
3616            continue;
3617        };
3618        let has_access = nodes.iter().copied().any(|candidate| {
3619            candidate.start_byte() >= name_node.end_byte()
3620                && candidate.end_byte() <= base_initializer.start_byte()
3621                && matches!(
3622                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3623                    "public" | "protected" | "private"
3624                )
3625        });
3626        if !has_access {
3627            continue;
3628        }
3629        let Some(base_node) = base_initializer
3630            .child_by_field_name("field")
3631            .or_else(|| base_initializer.named_child(0))
3632        else {
3633            continue;
3634        };
3635        let Some(base) = recovered_malformed_base_name(base_node, source) else {
3636            continue;
3637        };
3638        let body = base_initializer
3639            .child_by_field_name("value")
3640            .or_else(|| {
3641                let mut cursor = base_initializer.walk();
3642                base_initializer
3643                    .named_children(&mut cursor)
3644                    .find(|child| child.kind() == "initializer_list")
3645            })
3646            .expect("initializer-list value checked above");
3647        let range = Range {
3648            start_byte: class_token.start_byte(),
3649            end_byte: body.end_byte(),
3650            start_line: class_token.start_position().row + 1,
3651            end_line: body.end_position().row + 1,
3652        };
3653        if recovered
3654            .iter()
3655            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3656                existing.name == name && existing.range == range
3657            })
3658        {
3659            continue;
3660        }
3661        let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3662            continue;
3663        };
3664        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3665            name,
3666            range,
3667            raw_supertypes: vec![base],
3668            base_facts: vec![base_fact],
3669            fragmented_body: match recovered_fragmented_export_body(body, range) {
3670                Some(fragmented) => fragmented,
3671                None => continue,
3672            },
3673        });
3674    }
3675    recovered
3676}
3677
3678fn lifted_function_like_export_class_namespace<'tree>(
3679    node: Node<'tree>,
3680    source: &str,
3681    ancestry: &ParentIndex<'tree>,
3682) -> Option<String> {
3683    // A long malformed body can embed the next exported class several levels
3684    // below a bogus top-level function_definition. Compare namespace evidence
3685    // against that top-level envelope, not only the recovered ERROR's direct
3686    // parent. The source tree still proves the same boundary: one earlier
3687    // malformed namespace and one later standalone closing brace.
3688    let mut anchor = node;
3689    let parent = loop {
3690        let parent = ancestry.parent(anchor)?;
3691        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3692            break parent;
3693        }
3694        anchor = parent;
3695    };
3696    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3697        sibling.start_byte() > anchor.end_byte()
3698            && sibling.kind() == "ERROR"
3699            && sibling.named_child_count() == 0
3700            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3701    });
3702    if !has_later_close {
3703        return None;
3704    }
3705    let candidates = parent
3706        .named_children(&mut parent.walk())
3707        .filter(|sibling| {
3708            sibling.kind() == "namespace_definition"
3709                && sibling.has_error()
3710                && sibling.end_byte() < anchor.start_byte()
3711        })
3712        .filter_map(|namespace| {
3713            namespace
3714                .child_by_field_name("name")
3715                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3716                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3717        })
3718        .collect::<Vec<_>>();
3719    let [namespace] = candidates.as_slice() else {
3720        return None;
3721    };
3722    Some(namespace.clone())
3723}
3724
3725pub(crate) fn recovered_function_like_export_class_pair_has_body(
3726    node: Node<'_>,
3727    source: &str,
3728    identifier: &str,
3729    range: &Range,
3730) -> bool {
3731    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3732        recovered.name == identifier
3733            && recovered.range.start_byte == range.start_byte
3734            && recovered.range.end_byte == range.end_byte
3735    })
3736}
3737
3738fn recovered_exported_class_has_body(
3739    node: Node<'_>,
3740    source: &str,
3741    expected_name: &str,
3742) -> Option<bool> {
3743    match node.kind() {
3744        "function_definition" => {
3745            let (class_node, name, _, _) =
3746                recover_exported_class_function_definition(node, source)?;
3747            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3748        }
3749        "declaration" | "field_declaration" => {
3750            let recovered = recover_exported_class_declaration(node, source)?;
3751            (recovered.name == expected_name).then(|| recovered.body.is_some())
3752        }
3753        _ => None,
3754    }
3755}
3756
3757/// One file's embedded export-macro class recovery, resolved once and keyed by
3758/// the `ERROR` node that mints each set.
3759///
3760/// [`recover_embedded_function_like_export_classes`] collects and sorts an
3761/// `ERROR` node's whole subtree, and the declaration-strength question asks it
3762/// once per class-like unit in the file. On a translation unit the parser could
3763/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
3764/// node spans most of the file -- that is one full subtree pass per class,
3765/// quadratic in the file's size, and it was 78% of that file's inverse scan
3766/// (#1496).
3767///
3768/// Keyed by byte span rather than node identity, so one analyzer generation's
3769/// re-parses of the same content share the index. A nested `ERROR` that shares
3770/// its parent's span recovers the same classes: the only node the parent adds
3771/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
3772#[derive(Default)]
3773pub struct CppRecoveredExportClassIndex {
3774    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3775}
3776
3777impl CppRecoveredExportClassIndex {
3778    pub fn build(root: Node<'_>, source: &str) -> Self {
3779        let mut by_error_node: HashMap<
3780            (usize, usize),
3781            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3782        > = HashMap::default();
3783        // One cursor for the whole-file walk, and the error question asked by
3784        // symbol rather than by `kind()` string (#3097).
3785        let mut cursor = root.walk();
3786        let mut stack = vec![root];
3787        while let Some(node) = stack.pop() {
3788            if node.is_error() {
3789                let recovered = recover_embedded_function_like_export_classes(node, source);
3790                if !recovered.is_empty() {
3791                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3792                }
3793            }
3794            stack.extend(node.named_children(&mut cursor));
3795        }
3796        Self { by_error_node }
3797    }
3798
3799    /// The bytes this index holds, for the analyzer cache's weight.
3800    pub fn approximate_size(&self) -> usize {
3801        self.by_error_node
3802            .values()
3803            .fold(0usize, |total, recovered| {
3804                recovered.iter().fold(
3805                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3806                    |acc, class| {
3807                        acc.saturating_add(std::mem::size_of::<
3808                            RecoveredEmbeddedFunctionLikeExportClass,
3809                        >())
3810                        .saturating_add(class.name.len())
3811                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3812                    },
3813                )
3814            })
3815    }
3816
3817    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3818        self.by_error_node
3819            .get(&(node.start_byte(), node.end_byte()))
3820            .is_some_and(|recovered| {
3821                recovered.iter().any(|class| {
3822                    class.name == identifier
3823                        && class.range.start_byte == range.start_byte
3824                        && class.range.end_byte == range.end_byte
3825                })
3826            })
3827    }
3828}
3829
3830// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3831// `recovered_class_body_at` pops while deciding whether a recovered class shape
3832// claims one declaration range. The count is deterministic for a given source,
3833// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3834// pins it directly instead of timing the walk, the way #2358 pinned the
3835// `remove_code_unit` scan.
3836#[cfg(any(test, feature = "test-support"))]
3837thread_local! {
3838    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3839        const { std::cell::Cell::new(0) };
3840}
3841
3842/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3843/// the calling thread since the last
3844/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3845#[cfg(any(test, feature = "test-support"))]
3846#[doc(hidden)]
3847pub fn recovered_class_body_node_visits_for_test() -> usize {
3848    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3849}
3850
3851/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3852#[cfg(any(test, feature = "test-support"))]
3853#[doc(hidden)]
3854pub fn reset_recovered_class_body_node_visits_for_test() {
3855    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3856}
3857
3858#[cfg(any(test, feature = "test-support"))]
3859fn record_recovered_class_body_visit() {
3860    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3861}
3862
3863#[cfg(not(any(test, feature = "test-support")))]
3864fn record_recovered_class_body_visit() {}
3865
3866/// Whether a recovered class shape named `identifier` owns `range`, and if so
3867/// whether that shape has a body.
3868///
3869/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3870/// forward declaration, and `None` means no recovered shape claims the range,
3871/// so the caller reads the plain `class_specifier` family instead. This is the
3872/// single definition of "does a recovered class have a body": the resolver's
3873/// declaration-strength answer and the navigation occurrence role both read it,
3874/// so an export-macro class is a definition on both paths.
3875///
3876/// The walk follows only the nodes whose span covers `range.start_byte`, which
3877/// is every node that can answer. Each recovered shape reports a range that
3878/// starts at the node's own start byte (the function-like export pair, whose
3879/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3880/// class, which the caller gates on an equal start) or at a token inside the
3881/// node (the embedded export class, keyed on its `class` token, and the
3882/// exported class wrapper, gated on containment here), and every one of them is
3883/// accepted only on an exact match with `range`. Descending everywhere instead
3884/// made one declaration-strength question cost a full pass over the file, so
3885/// asking it once per reference was quadratic in file size: 80% of the 385 s
3886/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3887/// (#1496).
3888pub(crate) fn recovered_class_body_at(
3889    recovered_export_classes: &CppRecoveredExportClassIndex,
3890    root: Node<'_>,
3891    source: &str,
3892    identifier: &str,
3893    range: &Range,
3894) -> Option<bool> {
3895    let covers_range_start = |node: &Node<'_>| {
3896        node.start_byte() <= range.start_byte
3897            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3898    };
3899    let mut stack = vec![root];
3900    let mut saw_forward = false;
3901    while let Some(node) = stack.pop() {
3902        record_recovered_class_body_visit();
3903        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3904        // so an unequal start settles it before the recovery reads the node's
3905        // children at all.
3906        if (node.start_byte() == range.start_byte
3907            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3908            || recovered_export_classes.claims(node, identifier, range)
3909            || (node.start_byte() == range.start_byte
3910                && recovered_fragmented_class_has_body(node, source, identifier, range))
3911        {
3912            return Some(true);
3913        }
3914        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3915            return Some(true);
3916        }
3917        // Macro-decorated exported classes are recovered from a malformed
3918        // function_definition/declaration wrapper. Their indexed class range starts at
3919        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3920        // may also extend the indexed range beyond the wrapper through trailing class
3921        // fragments. Match the structured container that owns the range start by its
3922        // recovered name instead of requiring identical boundaries.
3923        if node.start_byte() <= range.start_byte
3924            && range.start_byte < node.end_byte()
3925            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3926        {
3927            if has_body {
3928                return Some(true);
3929            }
3930            saw_forward = true;
3931            continue;
3932        }
3933        let mut cursor = node.walk();
3934        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3935    }
3936    saw_forward.then_some(false)
3937}
3938
3939/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3940/// head of `identifier`'s member list at `range`.
3941///
3942/// An aggregate whose member list opens with a field-list macro invocation
3943/// never forms a specifier, so the ordinary strength read finds no specifier
3944/// node at the indexed range and answers `Unknown`. A complete definition
3945/// recovered this way then loses the defining-type choice to a forward
3946/// declaration of the same tag in another header, and every member the
3947/// definition declares becomes unreachable through it: libuv's
3948/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3949/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3950/// proves the body, so it answers for its own ranges.
3951fn recovered_collapsed_aggregate_has_body(
3952    node: Node<'_>,
3953    source: &str,
3954    identifier: &str,
3955    range: &Range,
3956) -> bool {
3957    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3958        head.key.start_byte() == range.start_byte
3959            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3960    };
3961    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3962        return true;
3963    }
3964    if !node.is_error() {
3965        return false;
3966    }
3967    let mut cursor = node.walk();
3968    let children = node.children(&mut cursor).collect::<Vec<_>>();
3969    children
3970        .iter()
3971        .enumerate()
3972        .filter(|(_, child)| {
3973            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3974        })
3975        .any(|(index, _)| {
3976            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3977        })
3978}
3979
3980/// Whether `node` is the base type displaced into the declarator field of an
3981/// export-macro class that tree-sitter represented as a declaration or
3982/// function definition.
3983///
3984/// Declaration extraction already recovers this exact malformed envelope as a
3985/// class and records the declarator as its base. Reference extraction must use
3986/// the same structural fact instead of treating the node as a function name.
3987pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3988    if !matches!(
3989        node.kind(),
3990        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3991    ) {
3992        return false;
3993    }
3994    if let Some(function) = node.parent().filter(|parent| {
3995        parent.kind() == "function_definition"
3996            && parent
3997                .child_by_field_name("declarator")
3998                .is_some_and(|declarator| same_node(declarator, node))
3999    }) {
4000        return recover_exported_class_function_definition(function, source)
4001            .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
4002    }
4003    let Some(initializer) = node.parent().filter(|parent| {
4004        parent.kind() == "init_declarator"
4005            && parent
4006                .child_by_field_name("declarator")
4007                .is_some_and(|declarator| same_node(declarator, node))
4008    }) else {
4009        return false;
4010    };
4011    initializer
4012        .parent()
4013        .filter(|parent| parent.kind() == "declaration")
4014        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
4015        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
4016}
4017
4018/// Recover the class item from a region reparse that still carries the
4019/// sentinel's synthetic function envelope.  An unknown class attribute can
4020/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
4021/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
4022/// then nested below that function, so direct class-child lookup is not enough.
4023struct CppSentinelReparsedClass<'tree> {
4024    declaration_node: Node<'tree>,
4025    name: String,
4026    body: Node<'tree>,
4027    raw_supertypes: Option<Vec<String>>,
4028    base_facts: Option<Vec<CppBaseSpecifierFact>>,
4029}
4030
4031fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
4032    let mut cursor = root.walk();
4033    root.named_children(&mut cursor)
4034        .find(|child| child.kind() != "comment")
4035        .filter(|child| child.kind() == "template_declaration")
4036}
4037
4038fn cpp_sentinel_reparsed_class<'tree>(
4039    root: Node<'tree>,
4040    template_node: Option<Node<'tree>>,
4041    source: &str,
4042    ancestry: &ParentIndex<'tree>,
4043) -> Option<CppSentinelReparsedClass<'tree>> {
4044    let container = template_node.unwrap_or(root);
4045    let mut cursor = container.walk();
4046    for child in container.named_children(&mut cursor) {
4047        if matches!(
4048            child.kind(),
4049            "class_specifier" | "struct_specifier" | "union_specifier"
4050        ) {
4051            let name = class_like_name(child, source, ancestry)?;
4052            let body = cpp_body_node(child)?;
4053            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
4054                .then(|| extract_cpp_supertypes(child, source));
4055            let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
4056                .then(|| extract_cpp_base_facts(child, source));
4057            return Some(CppSentinelReparsedClass {
4058                declaration_node: child,
4059                name,
4060                body,
4061                raw_supertypes,
4062                base_facts,
4063            });
4064        }
4065        if child.kind() == "declaration"
4066            && let Some(class_node) = first_class_like_child(child)
4067        {
4068            let name = class_like_name(class_node, source, ancestry)?;
4069            let body = cpp_body_node(class_node)?;
4070            let raw_supertypes =
4071                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4072                    .then(|| extract_cpp_supertypes(class_node, source));
4073            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4074                .then(|| extract_cpp_base_facts(class_node, source));
4075            return Some(CppSentinelReparsedClass {
4076                declaration_node: class_node,
4077                name,
4078                body,
4079                raw_supertypes,
4080                base_facts,
4081            });
4082        }
4083        // Only when the nested class item carries its own body. A bodyless
4084        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
4085        // a function definition -- is the export-macro shape recovered by the
4086        // next arm, and must fall through to it rather than abort the search.
4087        if child.kind() == "function_definition"
4088            && let Some(class_node) = first_class_like_child(child)
4089            && let Some(body) = cpp_body_node(class_node)
4090            && let Some(name) = class_like_name(class_node, source, ancestry)
4091        {
4092            let raw_supertypes =
4093                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4094                    .then(|| extract_cpp_supertypes(class_node, source));
4095            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4096                .then(|| extract_cpp_base_facts(class_node, source));
4097            return Some(CppSentinelReparsedClass {
4098                declaration_node: class_node,
4099                name,
4100                body,
4101                raw_supertypes,
4102                base_facts,
4103            });
4104        }
4105        if child.kind() == "function_definition"
4106            && let Some((_, name, raw_supertypes, base_facts)) =
4107                recover_exported_class_function_definition(child, source)
4108        {
4109            let body = cpp_body_node(child)?;
4110            return Some(CppSentinelReparsedClass {
4111                declaration_node: child,
4112                name,
4113                body,
4114                raw_supertypes,
4115                base_facts,
4116            });
4117        }
4118    }
4119    None
4120}
4121
4122fn recovered_postfix_export_macro_base(
4123    node: Node<'_>,
4124    type_node: Node<'_>,
4125    declarator: Node<'_>,
4126    source: &str,
4127) -> Option<String> {
4128    recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
4129        .and_then(|base| recovered_malformed_base_name(base, source))
4130}
4131
4132fn recovered_postfix_export_macro_base_node<'tree>(
4133    node: Node<'tree>,
4134    type_node: Node<'tree>,
4135    declarator: Node<'tree>,
4136    source: &str,
4137) -> Option<Node<'tree>> {
4138    let mut cursor = node.walk();
4139    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
4140        child.kind() == "ERROR"
4141            && child.start_byte() >= type_node.end_byte()
4142            && child.end_byte() <= declarator.start_byte()
4143            && postfix_export_macro_inheritance(*child, source)
4144    });
4145    malformed_clauses.next()?;
4146    if malformed_clauses.next().is_some() {
4147        return None;
4148    }
4149    Some(declarator)
4150}
4151
4152fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
4153    let mut macro_count = 0;
4154    let mut colon_count = 0;
4155    let mut access_count = 0;
4156    for index in 0..node.child_count() {
4157        let Some(child) = node.child(index) else {
4158            return false;
4159        };
4160        match child.kind() {
4161            "identifier" | "type_identifier" if child.is_named() => {
4162                let candidate = normalize_cpp_whitespace(node_text(child, source));
4163                if !cpp_export_macro_token(&candidate) {
4164                    return false;
4165                }
4166                macro_count += 1;
4167            }
4168            ":" if !child.is_named() => colon_count += 1,
4169            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
4170            _ => return false,
4171        }
4172    }
4173    macro_count == 1 && colon_count == 1 && access_count == 1
4174}
4175
4176fn recovered_single_base_after_declarator_node<'tree>(
4177    node: Node<'tree>,
4178    declarator: Node<'tree>,
4179    source: &str,
4180) -> Option<Node<'tree>> {
4181    let body_start = node
4182        .child_by_field_name("body")
4183        .map(|body| body.start_byte())
4184        .unwrap_or(node.end_byte());
4185    let mut cursor = node.walk();
4186    let mut bases = node
4187        .named_children(&mut cursor)
4188        .filter(|child| {
4189            child.kind() == "ERROR"
4190                && child.start_byte() >= declarator.end_byte()
4191                && child.end_byte() <= body_start
4192        })
4193        .filter_map(|error| {
4194            displaced_exported_class_name(error, source)?;
4195            let mut error_cursor = error.walk();
4196            error
4197                .named_children(&mut error_cursor)
4198                .find(|child| recovered_malformed_base_name(*child, source).is_some())
4199        });
4200    let base = bases.next()?;
4201    bases.next().is_none().then_some(base)
4202}
4203
4204fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
4205    (0..node.child_count()).any(|index| {
4206        node.child(index).is_some_and(|child| {
4207            child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
4208        })
4209    })
4210}
4211
4212fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
4213    matches!(name, "public" | "protected" | "private")
4214}
4215
4216pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
4217    recover_exported_class_function_definition(node, source).is_some()
4218}
4219
4220fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
4221    matches!(
4222        kind,
4223        "ERROR"
4224            | "preproc_if"
4225            | "preproc_ifdef"
4226            | "preproc_ifndef"
4227            | "preproc_else"
4228            | "preproc_elif"
4229    ) || (kind == "labeled_statement" && in_class_scope)
4230}
4231
4232pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
4233    if node.kind() != "declaration" {
4234        return false;
4235    }
4236    let mut ancestor = node.parent();
4237    while let Some(container) = ancestor {
4238        match container.kind() {
4239            "compound_statement" => {
4240                return container.parent().is_some_and(|class_container| {
4241                    is_recovered_exported_class_container(class_container, source)
4242                });
4243            }
4244            // These containers preserve ScopeInfo in visit_node. declaration_list is
4245            // the body container selected for a linkage specification.
4246            "template_declaration" | "linkage_specification" | "declaration_list" => {}
4247            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
4248            _ => return false,
4249        }
4250        ancestor = container.parent();
4251    }
4252    false
4253}
4254
4255fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
4256    let body_start = node
4257        .child_by_field_name("body")
4258        .map(|body| body.start_byte())
4259        .unwrap_or(node.end_byte());
4260    let mut stack = Vec::new();
4261    for index in (0..node.named_child_count()).rev() {
4262        let Some(child) = node.named_child(index) else {
4263            continue;
4264        };
4265        if child.start_byte() >= body_start {
4266            continue;
4267        }
4268        stack.push(child);
4269    }
4270
4271    let mut best = None;
4272    while let Some(current) = stack.pop() {
4273        if matches!(current.kind(), "identifier" | "type_identifier") {
4274            let name = normalize_cpp_whitespace(node_text(current, source));
4275            if !name.is_empty()
4276                && !cpp_export_macro_token(&name)
4277                && !matches!(name.as_str(), "class" | "struct" | "union")
4278            {
4279                best = Some(name);
4280            }
4281            continue;
4282        }
4283
4284        for index in (0..current.named_child_count()).rev() {
4285            if let Some(child) = current.named_child(index)
4286                && child.start_byte() < body_start
4287            {
4288                stack.push(child);
4289            }
4290        }
4291    }
4292    best
4293}
4294
4295fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4296    if node.kind() == "declaration"
4297        && node
4298            .child_by_field_name("type")
4299            .or_else(|| first_class_like_child(node))
4300            .is_some_and(|type_node| {
4301                matches!(
4302                    type_node.kind(),
4303                    "class_specifier" | "struct_specifier" | "union_specifier"
4304                )
4305            })
4306        && let Some(name) = node
4307            .child_by_field_name("declarator")
4308            .and_then(|declarator| declarator_name_from_node(declarator, source))
4309        && !cpp_export_macro_token(&name)
4310    {
4311        return Some(name);
4312    }
4313
4314    if node.kind() == "function_definition"
4315        && node.child_by_field_name("type").is_some_and(|type_node| {
4316            matches!(
4317                type_node.kind(),
4318                "class_specifier" | "struct_specifier" | "union_specifier"
4319            )
4320        })
4321        && let Some(name) = node
4322            .child_by_field_name("declarator")
4323            .and_then(|declarator| direct_identifier_name(declarator, source))
4324        && !cpp_export_macro_token(&name)
4325    {
4326        return Some(name);
4327    }
4328
4329    let class_node = if matches!(
4330        node.kind(),
4331        "class_specifier" | "struct_specifier" | "union_specifier"
4332    ) {
4333        node
4334    } else {
4335        first_class_like_child(node)?
4336    };
4337    class_like_name_from_children(class_node, source)
4338}
4339
4340/// Normalize the text of a directly selected C++ identifier node.
4341pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
4342    if !matches!(
4343        node.kind(),
4344        "identifier" | "field_identifier" | "type_identifier"
4345    ) {
4346        return None;
4347    }
4348    let name = normalize_cpp_whitespace(node_text(node, source));
4349    (!name.is_empty()).then_some(name)
4350}
4351
4352fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4353    if let Some(name) = direct_identifier_name(node, source) {
4354        return Some(name);
4355    }
4356    let mut cursor = node.walk();
4357    node.named_children(&mut cursor)
4358        .find_map(|child| declarator_name_from_node(child, source))
4359}
4360
4361fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
4362    let mut cursor = node.walk();
4363    node.named_children(&mut cursor).find(|child| {
4364        matches!(
4365            child.kind(),
4366            "class_specifier" | "struct_specifier" | "union_specifier"
4367        )
4368    })
4369}
4370
4371/// Push a container's children as a `Siblings` cursor rather than snapshotting
4372/// them all with one shared scope: children are visited one at a time so a
4373/// `using namespace X;` sibling can affect the scope threaded to the siblings
4374/// that textually follow it (issue #1093).
4375fn push_cpp_container_work<'tree>(
4376    node: Node<'tree>,
4377    scope: ScopeInfo,
4378    stack: &mut Vec<CppWork<'tree>>,
4379) {
4380    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
4381}
4382
4383/// Materialize one selected named-child range with a tree-sitter cursor. The
4384/// cursor advances linearly across the parent's concrete children; repeatedly
4385/// asking for `named_child(index)` is quadratic on very wide generated nodes.
4386fn push_cpp_sibling_range<'tree>(
4387    parent: Node<'tree>,
4388    start_index: usize,
4389    end_index: usize,
4390    scope: ScopeInfo,
4391    stack: &mut Vec<CppWork<'tree>>,
4392) {
4393    let mut cursor = parent.walk();
4394    let children = parent
4395        .named_children(&mut cursor)
4396        .skip(start_index)
4397        .take(end_index.saturating_sub(start_index))
4398        .collect::<Vec<_>>()
4399        .into_iter();
4400    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
4401}
4402
4403/// Advance a `Siblings` cursor by one child: dispatch the current child under
4404/// the scope accumulated from its *earlier* siblings, then push a
4405/// continuation for the remaining siblings carrying the scope updated for
4406/// *this* child (only `using namespace X;` directives change it). Pushing the
4407/// continuation before the current child's own node work means the current
4408/// child's subtree fully drains (LIFO) before the next sibling is visited,
4409/// preserving left-to-right order.
4410fn advance_cpp_siblings<'tree>(
4411    mut siblings: CppSiblingsWork<'tree>,
4412    source: &str,
4413    stack: &mut Vec<CppWork<'tree>>,
4414) {
4415    let Some(child) = siblings.children.next() else {
4416        return;
4417    };
4418    let current_scope = siblings.scope.clone();
4419    if let Some(namespace) = cpp_using_namespace_target(child, source) {
4420        siblings.scope.visible_using_namespaces.push(namespace);
4421    }
4422    push_cpp_using_declaration_binding(&mut siblings.scope, child, source);
4423    if !siblings.children.as_slice().is_empty() {
4424        stack.push(CppWork::Siblings(siblings));
4425    }
4426    stack.push(CppWork::Node(CppNodeWork {
4427        node: child,
4428        scope: current_scope,
4429    }));
4430}
4431
4432/// The namespace target of a `using namespace X;` directive, or `None` for
4433/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
4434/// kind. Distinguished structurally by the presence of the grammar's literal
4435/// `namespace` keyword token among the node's children -- not by inspecting
4436/// source text -- so it never misreads a member-importing using-declaration
4437/// as a namespace directive.
4438fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4439    if node.kind() != "using_declaration" {
4440        return None;
4441    }
4442    let mut cursor = node.walk();
4443    let is_namespace_directive = node
4444        .children(&mut cursor)
4445        .any(|child| child.kind() == "namespace");
4446    if !is_namespace_directive {
4447        return None;
4448    }
4449    let target = node.named_child(0)?;
4450    // A leading `::` is the explicit-global marker, not part of the namespace
4451    // path (`using namespace ::std::chrono;`). Drop that AST token before
4452    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
4453    // keeps: storing the marker verbatim desynced the legacy package string from
4454    // the FqName bridge, which splits on `::` and drops the empty leading
4455    // component, tripping the package/short boundary assert when a bare-owner
4456    // out-of-line definition borrowed the directive's namespace (#1093 path).
4457    let start = target
4458        .child(0)
4459        .filter(|child| !child.is_named() && child.kind() == "::")
4460        .map_or(target.start_byte(), |marker| marker.end_byte());
4461    let text = normalize_cpp_whitespace(
4462        source
4463            .get(start..target.end_byte())
4464            .expect("using-directive target covers one source range"),
4465    );
4466    (!text.is_empty()).then_some(text)
4467}
4468
4469/// Record `node` in `scope` when it is a namespace-scope `using ns::Type;`
4470/// declaration, so later siblings see the binding it introduces.
4471///
4472/// The target is read from the declaration's structured qualified name. A
4473/// using directive (`using namespace ns;`), a `using enum`, a member using
4474/// inside a class body, and a target with a template-id component introduce
4475/// no class binding this extraction can use, so they are skipped. A relative
4476/// target inside a namespace resolves against that namespace, dropping a
4477/// prefix that re-states it, the same reading `split_cpp_name` gives a
4478/// written out-of-line qualifier: a member definition must sit in a namespace
4479/// that encloses its class.
4480fn push_cpp_using_declaration_binding(scope: &mut ScopeInfo, node: Node<'_>, source: &str) {
4481    if node.kind() != "using_declaration" || scope.class_unit.is_some() {
4482        return;
4483    }
4484    let mut cursor = node.walk();
4485    if node
4486        .children(&mut cursor)
4487        .any(|child| matches!(child.kind(), "namespace" | "enum"))
4488    {
4489        return;
4490    }
4491    let Some((components, explicitly_global)) = node
4492        .named_child(0)
4493        .and_then(|target| structured_cpp_qualified_components(target, source))
4494    else {
4495        return;
4496    };
4497    let Some((name, namespace)) = components.split_last() else {
4498        return;
4499    };
4500    if namespace.is_empty() || components.iter().any(|component| component.is_template_id) {
4501        return;
4502    }
4503    let namespace = namespace
4504        .iter()
4505        .map(|component| component.name.as_str())
4506        .collect::<Vec<_>>();
4507    let target_package = if explicitly_global || scope.package_name.is_empty() {
4508        namespace.join("::")
4509    } else {
4510        let relative = strip_redundant_namespace_prefix(&namespace, &scope.package_name);
4511        std::iter::once(scope.package_name.as_str())
4512            .chain(relative.iter().copied())
4513            .collect::<Vec<_>>()
4514            .join("::")
4515    };
4516    Arc::make_mut(&mut scope.visible_using_declarations).push(CppUsingDeclarationBinding {
4517        scope_package: scope.package_name.clone(),
4518        name: name.name.clone(),
4519        target_package,
4520    });
4521}
4522
4523/// Tokens that open a construct only a matching later token closes.
4524const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4525
4526/// The opening tokens of the constructs a C/C++ parse left open at the end of
4527/// the file, in source order (#3501). Empty unless the tree's root is an ERROR
4528/// node.
4529///
4530/// The grammar has no preprocessor, so a construct opened in one conditional
4531/// branch and closed in another stays open: `#if 0` / `extern "C" {` /
4532/// `#endif` leaves its `{` unmatched until a later `#if 0` / `}` / `#endif`.
4533/// One such pair repairs with a single MISSING token. With more of them, the
4534/// parse version tree-sitter keeps reaches the end of input in its error
4535/// state, and tree-sitter wraps every subtree on that version's stack in one
4536/// ERROR node and returns it as the root (`recover_eof` in its `parser.c`).
4537/// The root's children are then exactly that stack: completed subtrees, and
4538/// the bare tokens of the constructs still waiting to be reduced when input
4539/// ran out. A reduced construct is a node of its own, so a bare opener among
4540/// the root's children is one the parse never closed. tdlib/td's copy of the
4541/// SQLite amalgamation parses this way: its root spans all 8,274,268 bytes,
4542/// and its outermost open construct is the `#ifndef SQLITEINT_H` include guard
4543/// at byte 19,718.
4544pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4545    assert!(
4546        root.parent().is_none(),
4547        "only a tree's root records what its parse left open"
4548    );
4549    if !root.is_error() {
4550        return Vec::new();
4551    }
4552    children_iter(root)
4553        .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4554        .collect()
4555}
4556
4557/// Record every declaration of `parsed` that has a range inside `recovery`
4558/// as recovered, unless a narrower recovery already claimed it, and mark the
4559/// reading partial when any declaration lies there (#3501).
4560///
4561/// `recovery` runs from the outermost construct the parse left open to the
4562/// end of the file. Everything before that construct was completed at file
4563/// scope, as in any parse. Everything after it sat on the parse stack above
4564/// an open construct when input ran out, so the scope and nesting the walk
4565/// gave it are a recovery claim rather than a reading of a completed tree,
4566/// and the file's analysis is partial whichever recovery recorded it.
4567fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4568    let recorded: HashSet<CodeUnit> = parsed
4569        .materialization_records
4570        .iter()
4571        .filter_map(|record| match record {
4572            MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4573            _ => None,
4574        })
4575        .collect();
4576    let mut left_open: Vec<(usize, CodeUnit)> = parsed
4577        .declarations()
4578        .iter()
4579        .filter_map(|unit| {
4580            let ranges = parsed.declaration_ranges(unit);
4581            let first = ranges.iter().map(|range| range.start_byte).min()?;
4582            let last = ranges.iter().map(|range| range.start_byte).max()?;
4583            (last >= recovery.start_byte).then(|| (first, unit.clone()))
4584        })
4585        .collect();
4586    if left_open.is_empty() {
4587        return;
4588    }
4589    parsed.completeness = ParseCompleteness::Partial;
4590    left_open.retain(|(_, unit)| !recorded.contains(unit));
4591    left_open.sort();
4592    for (_, unit) in left_open {
4593        parsed
4594            .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4595    }
4596}
4597
4598/// Extract all requested dialect readings from one primary AST event stream.
4599/// Declaration schedulers retain their own admission, recovery and sibling order;
4600/// structural facts, includes and identifiers are interpreted once for the tree.
4601pub(crate) fn parse_cpp_readings<'tree>(
4602    file: &ProjectFile,
4603    source: &str,
4604    root: Node<'tree>,
4605    ancestry: &ParentIndex<'tree>,
4606    dialects: &[LanguageDialect],
4607) -> Vec<ParsedFile> {
4608    parse_cpp_readings_with_object_macro_fields(
4609        file,
4610        source,
4611        root,
4612        ancestry,
4613        dialects,
4614        HashMap::default(),
4615    )
4616}
4617
4618pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4619    file: &ProjectFile,
4620    source: &str,
4621    root: Node<'tree>,
4622    ancestry: &ParentIndex<'tree>,
4623    dialects: &[LanguageDialect],
4624    object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4625) -> Vec<ParsedFile> {
4626    assert!(
4627        dialects.len() == 1
4628            || (dialects.len() == 2
4629                && dialects[0] != LanguageDialect::CppC
4630                && dialects[1] == LanguageDialect::CppC),
4631        "one primary dialect, optionally followed by its C alternative"
4632    );
4633    let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4634    let properties = RefCell::new(HashMap::default());
4635    let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4636    let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4637    let mut using_namespaces = Vec::new();
4638    let mut namespace_aliases = Vec::new();
4639    let mut readings: Vec<_> = dialects
4640        .iter()
4641        .map(|_| ParsedFile::new(String::new()))
4642        .collect();
4643    let mut visitors: Vec<_> = readings
4644        .iter_mut()
4645        .zip(dialects)
4646        .map(|(parsed, dialect)| CppVisitor {
4647            file,
4648            source,
4649            parsed,
4650            source_collector: &collector,
4651            source_properties: &properties,
4652            source_dependencies: &dependencies,
4653            primary_ancestry: ancestry,
4654            primary_root_id: root.id(),
4655            source_links: HashMap::default(),
4656            source_declarations: HashSet::default(),
4657            preprocessor_context_cache: Default::default(),
4658            c_tag_semantics: *dialect == LanguageDialect::CppC,
4659            c_tag_scope_witness: false,
4660            recovered_class_sibling_scopes: HashMap::default(),
4661            consumed_fragment_regions: Vec::new(),
4662            orphaned_namespaces: &orphaned_namespaces,
4663            partitioned_regions: Vec::new(),
4664            namespace_forward_scans: HashMap::default(),
4665            recovery_captures: Vec::new(),
4666            active_source_context: None,
4667            object_macro_fields: object_macro_fields.clone(),
4668            ambiguous_object_macro_fields: HashSet::default(),
4669        })
4670        .collect();
4671    let mut declaration_stacks: Vec<_> = dialects
4672        .iter()
4673        .map(|_| {
4674            vec![CppWork::Container(CppContainer {
4675                node: root,
4676                scope: ScopeInfo {
4677                    package_name: String::new(),
4678                    module: None,
4679                    class_unit: None,
4680                    template_signature: None,
4681                    template_metadata: None,
4682                    declarations_are_fields: false,
4683                    recovered_specialization_member_scope: false,
4684                    visible_using_namespaces: Vec::new(),
4685                    local_block: None,
4686                    visible_using_declarations: Arc::default(),
4687                },
4688            })]
4689        })
4690        .collect();
4691    let kinds = CompiledKinds::compile(
4692        &tree_sitter_cpp::LANGUAGE.into(),
4693        CPP_STRUCTURAL_SPEC.kind_table(),
4694    );
4695    // Forward macro names affect calls before their definitions and require the
4696    // existing bounded preprocessor-only context prepass.
4697    let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4698    let mut structural = StructuralFactCollector::new(
4699        &CPP_STRUCTURAL_SPEC,
4700        source,
4701        &context,
4702        brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4703        usize::MAX,
4704        None,
4705    );
4706    let mut imports = Vec::new();
4707    let mut includes = Vec::new();
4708    let mut expressions = Vec::new();
4709    let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4710    let mut statement_facts = StatementFactRows::default();
4711    let mut identifiers = HashSet::default();
4712    let mut entered = HashSet::default();
4713    let mut primary_ordinals = HashMap::default();
4714    let mut stack = vec![(root, None)];
4715    while let Some((node, parent)) = stack.pop() {
4716        assert!(entered.insert(node.id()), "one primary event per AST node");
4717        primary_ordinals.insert(node.id(), primary_ordinals.len());
4718        dependencies.borrow_mut().primary.enter(node, source);
4719        let mut child_parent = parent;
4720        if node.is_named() {
4721            crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4722            if let Some(namespace) = cpp_using_namespace_target(node, source) {
4723                using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4724            }
4725            if let Some(alias) = crate::source_facts::capture_namespace_alias(
4726                node,
4727                source,
4728                ancestry,
4729                &orphaned_namespaces,
4730                &mut collector.borrow_mut(),
4731            ) {
4732                namespace_aliases.push(alias);
4733            }
4734            if let Some(raw_kind) = kinds.kind_of(&node)
4735                && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4736            {
4737                let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4738                    node,
4739                    raw_kind,
4740                    parent.map(|id| structural.normalized_kind(id)),
4741                    source,
4742                    &context,
4743                );
4744                let mut source_facts = collector.borrow_mut();
4745                let id = structural
4746                    .enter(node, kind, parent, &mut source_facts)
4747                    .expect("unbounded C++ structural collector");
4748                let mut sink = structural.role_sink(&mut source_facts);
4749                CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4750                structural
4751                    .accept_roles(id, sink.into_parts())
4752                    .expect("unbounded C++ structural roles");
4753                child_parent = Some(id);
4754            }
4755            if let Some(fact) = crate::source_facts::capture_expression_fact(
4756                node,
4757                source,
4758                collector.borrow_mut().intern_node(node),
4759                &named_record_sizes,
4760            ) {
4761                expressions.push(fact);
4762            }
4763            if node.kind() == "preproc_include" {
4764                let raw = normalize_cpp_whitespace(node_text(node, source));
4765                if !raw.is_empty() {
4766                    let occurrence = collector.borrow_mut().intern_node(node);
4767                    let target = node.child_by_field_name("path").and_then(|path| {
4768                        let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4769                        let target = collector.borrow_mut().intern_node(path);
4770                        includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4771                            declaration: occurrence,
4772                            target,
4773                            path: value.to_owned(),
4774                            quoted,
4775                        });
4776                        Some(target)
4777                    });
4778                    imports.push(SourceImportFact::from_import(
4779                        cpp_include_info(raw),
4780                        occurrence,
4781                        target,
4782                        None,
4783                        Vec::new(),
4784                    ));
4785                }
4786            }
4787            if matches!(
4788                node.kind(),
4789                "type_identifier" | "identifier" | "qualified_identifier"
4790            ) {
4791                let text = node_text(node, source).trim();
4792                if !text.is_empty() {
4793                    identifiers.insert(text.to_owned());
4794                }
4795            }
4796        }
4797        for index in 0..visitors.len() {
4798            // Delay the alternative policy until the primary walk encounters a
4799            // tag whose scope can differ. Its saved declaration stack catches up
4800            // through already-entered events, including recovery, without a
4801            // second AST traversal. A false witness performs no C declaration work.
4802            if index != 0 && !visitors[0].c_tag_scope_witness {
4803                continue;
4804            }
4805            let visitor = &mut visitors[index];
4806            advance_primary_declarations(
4807                visitor,
4808                &mut declaration_stacks[index],
4809                &entered,
4810                ancestry,
4811            );
4812            visitor.drain_partitioned_regions();
4813        }
4814        let mut cursor = node.walk();
4815        let children: Vec<_> = node
4816            .children(&mut cursor)
4817            .map(|child| (child, child_parent))
4818            .collect();
4819        stack.extend(children.into_iter().rev());
4820    }
4821    let reading_count = if visitors[0].c_tag_scope_witness {
4822        visitors.len()
4823    } else {
4824        1
4825    };
4826    visitors.truncate(reading_count);
4827    declaration_stacks.truncate(reading_count);
4828    assert!(
4829        declaration_stacks.iter().all(Vec::is_empty),
4830        "all primary declaration events were consumed"
4831    );
4832    for visitor in &mut visitors {
4833        visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4834            .into_iter()
4835            .flat_map(|(unit, declarations)| {
4836                declarations
4837                    .into_iter()
4838                    .map(move |declaration| (declaration, unit.clone()))
4839            })
4840            .collect();
4841    }
4842    let dialect_declarations: Vec<_> = visitors
4843        .iter()
4844        .map(|visitor| visitor.source_declarations.clone())
4845        .collect();
4846    drop(visitors);
4847    readings.truncate(reading_count);
4848    dependencies
4849        .borrow_mut()
4850        .finish_primary(&mut properties.borrow_mut());
4851    // Preserve the existing malformed quoted-directive recovery, but publish its
4852    // explicit source occurrence into the same import family as parsed directives.
4853    recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4854    let primary_occurrence_ordinals: HashMap<_, _> = collector
4855        .borrow()
4856        .primary_node_occurrences()
4857        .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4858        .collect();
4859    let occurrences = collector.into_inner().finish();
4860    let generic_imports: Vec<_> = (0..imports.len())
4861        .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4862        .collect();
4863    let source_facts = ParsedSourceFacts {
4864        cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4865            includes,
4866            declarations: {
4867                let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4868                facts.sort_by_key(|fact| fact.declaration.get());
4869                facts
4870            },
4871            using_namespaces,
4872            namespace_aliases,
4873            expressions,
4874            statement_facts,
4875        }),
4876        go: None,
4877        java: None,
4878        js_ts: None,
4879        php: None,
4880        scala: None,
4881        ruby: None,
4882        python: None,
4883        source_bytes: source.len(),
4884        occurrences,
4885        structural: structural
4886            .finish(root)
4887            .expect("C++ structural collection finishes"),
4888        native_site_occurrences: Vec::new(),
4889        native_declaration_sources: Vec::new(),
4890        declaration_visibilities: None,
4891        rust_declaration_properties: Vec::new(),
4892        rust_modules: None,
4893        rust_types: Vec::new(),
4894        rust_items: Default::default(),
4895        imports,
4896        generic_imports,
4897        rust_import_contexts: Vec::new(),
4898    };
4899    for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4900        parsed.finalize_deferred_replacements();
4901        let links = std::mem::take(&mut parsed.source_declaration_units);
4902        parsed.source_declaration_units = links
4903            .into_iter()
4904            .filter(|(_, unit)| parsed.contains_declaration(unit))
4905            .collect();
4906        parsed.imports = source_facts
4907            .imports
4908            .iter()
4909            .map(|import| import.import_info(&source_facts.occurrences))
4910            .collect();
4911        parsed.type_identifiers = identifiers.clone();
4912        parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4913            &source_facts,
4914            &declarations,
4915            &primary_occurrence_ordinals,
4916            &mut parsed.source_declaration_units,
4917            &mut parsed.source_declaration_metadata,
4918        ));
4919    }
4920    if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4921        // The recovery runs to the end of the file, which is how a reader of
4922        // the record tells this recovery from a local one (#3501).
4923        let recovery = Range {
4924            start_byte: outermost.start_byte(),
4925            end_byte: source.len(),
4926            start_line: outermost.start_position().row + 1,
4927            end_line: root.end_position().row + 1,
4928        };
4929        for parsed in &mut readings {
4930            record_declarations_left_open(parsed, recovery);
4931        }
4932    }
4933    readings
4934}
4935
4936fn advance_primary_declarations<'tree>(
4937    visitor: &mut CppVisitor<'_, '_>,
4938    stack: &mut Vec<CppWork<'tree>>,
4939    entered: &HashSet<usize>,
4940    ancestry: &ParentIndex<'tree>,
4941) {
4942    while let Some(work) = stack.pop() {
4943        match work {
4944            CppWork::Container(container) => {
4945                push_cpp_container_work(container.node, container.scope, stack)
4946            }
4947            CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4948            CppWork::Node(work) => {
4949                if !entered.contains(&work.node.id()) {
4950                    stack.push(CppWork::Node(work));
4951                    return;
4952                }
4953                if !visitor.node_is_inside_consumed_fragment(work.node) {
4954                    visitor.visit_node(work.node, &work.scope, stack, ancestry);
4955                }
4956            }
4957        }
4958    }
4959}
4960
4961#[derive(Clone)]
4962struct CppActiveSourceContext {
4963    guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4964    namespace: Option<String>,
4965    child_namespace: Option<String>,
4966    outer_lexical_scope: Vec<String>,
4967    child_lexical_scope: Vec<String>,
4968    family: Option<SourceOccurrenceId>,
4969    exhaustive_family: Option<SourceOccurrenceId>,
4970    callable_guard_completion_byte: Option<usize>,
4971}
4972
4973/// One owned container reparse deferred until the primary declaration walk has
4974/// reached its source position. The tree and context are owned so the deferred
4975/// walk never retains a `Node` borrowed from a temporary recovery tree.
4976pub struct CppPartitionedRegion {
4977    tree: Tree,
4978    range: std::ops::Range<usize>,
4979    scope: ScopeInfo,
4980    recovery: Range,
4981    source_context: Option<CppActiveSourceContext>,
4982}
4983
4984pub struct CppVisitor<'a, 'source> {
4985    pub file: &'a ProjectFile,
4986    pub source: &'source str,
4987    pub parsed: &'a mut ParsedFile,
4988    pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4989    source_properties: &'a RefCell<
4990        HashMap<
4991            SourceDeclarationId,
4992            brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4993        >,
4994    >,
4995    source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4996    pub primary_ancestry: &'a ParentIndex<'a>,
4997    pub primary_root_id: usize,
4998    source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4999    source_declarations: HashSet<SourceDeclarationId>,
5000    preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
5001    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
5002    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
5003    ///
5004    /// C has no nested tag scope: a struct/union/enum tag declared inside
5005    /// another aggregate's member list has the scope of the outer declaration
5006    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
5007    /// therefore declares a file-scope `inner` that a later file-scope
5008    /// `struct inner *p;` legitimately references, where C++ would make the
5009    /// same shape a nested class `outer::inner`. Headers carry no compilation
5010    /// language of their own and keep the conservative C++ interpretation.
5011    pub c_tag_semantics: bool,
5012    /// Sticky evidence that this C++ walk reached a declaration whose scope
5013    /// would change under C tag semantics, including visitor recovery work.
5014    pub(crate) c_tag_scope_witness: bool,
5015    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
5016    /// Byte regions whose contents were re-owned by a fragmented export-class
5017    /// recovery (#938): the scattered members between the fragmented
5018    /// declaration and its displaced closing brace are indexed as members of
5019    /// the recovered class by the region reparse, so the ordinary sibling walk
5020    /// must not ALSO index them as top-level declarations (that double-indexing
5021    /// made a scattered nested class ambiguous between `Inner` and
5022    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
5023    /// linear scan at visit time is fine.
5024    pub consumed_fragment_regions: Vec<(usize, usize)>,
5025    /// Lexical namespace corrections and brace boundaries for declarations
5026    /// displaced by parse recovery (issues #1537 and #3087). The parsed
5027    /// ancestors can stop short or extend past their true closes; see
5028    /// [`CppVisitor::recovered_namespace_scope`].
5029    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
5030    /// Owned reparses waiting for the outer work loop. Partitioning another
5031    /// swallowed class in a tail must not grow the Rust call stack.
5032    pub partitioned_regions: Vec<CppPartitionedRegion>,
5033    /// The namespace forward declarations already folded out of each tree this
5034    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
5035    /// Empty until the first question, which the overwhelming majority of files
5036    /// never ask.
5037    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
5038    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
5039    /// happen to the declaration set, innermost last. Empty outside a recovery
5040    /// reparse, which is almost always (#2787).
5041    pub recovery_captures: Vec<CppRecoveryCapture>,
5042    active_source_context: Option<CppActiveSourceContext>,
5043    /// Object-like field-list macros defined earlier in this source. Their
5044    /// replacements are parsed structurally and materialized under each
5045    /// invoking aggregate; no source-text expansion is used.
5046    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
5047    /// Names whose active replacement is not a unique structured field list.
5048    /// A later `#undef` resets the ambiguity; another `#define` does not.
5049    pub ambiguous_object_macro_fields: HashSet<String>,
5050}
5051
5052/// One source-order event from an object-like field-list macro environment.
5053///
5054/// The event stream lets an include-closure consumer preserve preprocessor
5055/// invalidation without guessing which parsed header happened to be visited
5056/// first. A conservative consumer may permanently block a name after an
5057/// `undef` when conditional include order is not provable.
5058#[derive(Clone, Debug, PartialEq, Eq)]
5059pub enum ObjectMacroFieldEvent {
5060    Define {
5061        name: String,
5062        replacement: ObjectMacroReplacement,
5063        conditional: bool,
5064    },
5065    Undef {
5066        name: String,
5067        conditional: bool,
5068    },
5069}
5070
5071/// Collect object-like field-list macro events in source order. Conditional
5072/// branches remain visible in the event stream: callers that cannot prove
5073/// branch selection can invalidate names conservatively instead of inventing
5074/// a replacement from one branch.
5075pub fn collect_cpp_object_macro_field_events<'tree>(
5076    root: Node<'tree>,
5077    source: &str,
5078) -> Vec<ObjectMacroFieldEvent> {
5079    let mut events = Vec::new();
5080    let mut stack = vec![root];
5081    while let Some(node) = stack.pop() {
5082        if node.kind() == "preproc_def"
5083            && let Some(name) = extract_macro_name(node, source)
5084        {
5085            let replacement = object_macro_replacement_of(node, source);
5086            events.push(ObjectMacroFieldEvent::Define {
5087                name,
5088                replacement,
5089                conditional: inside_preprocessor_conditional(node),
5090            });
5091        } else if is_cpp_undef_directive(node, source)
5092            && let Some(argument) = node.child_by_field_name("argument")
5093        {
5094            events.push(ObjectMacroFieldEvent::Undef {
5095                name: node_text(argument, source).trim().to_string(),
5096                conditional: inside_preprocessor_conditional(node),
5097            });
5098        }
5099        let mut cursor = node.walk();
5100        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5101        stack.extend(children.into_iter().rev());
5102    }
5103    events
5104}
5105
5106fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5107    let mut current = node.parent();
5108    while let Some(parent) = current {
5109        if is_cpp_preprocessor_conditional(parent) {
5110            return true;
5111        }
5112        current = parent.parent();
5113    }
5114    false
5115}
5116
5117fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5118    node.kind() == "preproc_call"
5119        && node
5120            .child_by_field_name("directive")
5121            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5122}
5123
5124impl<'a, 'source> CppVisitor<'a, 'source> {
5125    fn apply_active_source_context(
5126        &self,
5127        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5128    ) {
5129        let Some(context) = self.active_source_context.as_ref() else {
5130            return;
5131        };
5132        fact.guard_requirements = fact
5133            .guard_requirements
5134            .as_ref()
5135            .and_then(crate::source_context::cpp_guard_set_to_runtime)
5136            .zip(context.guards.as_ref())
5137            .and_then(|(local, outer)| {
5138                crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5139            })
5140            .as_ref()
5141            .map(crate::source_context::cpp_guard_set_from_runtime);
5142        if fact.callable_guards.is_some() {
5143            fact.callable_guards = fact
5144                .callable_guards
5145                .as_ref()
5146                .and_then(crate::source_context::cpp_guard_set_to_runtime)
5147                .zip(context.guards.as_ref())
5148                .and_then(|(local, outer)| {
5149                    crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5150                })
5151                .as_ref()
5152                .map(crate::source_context::cpp_guard_set_from_runtime);
5153        }
5154        fact.callable_guard_completion_byte = fact
5155            .callable_guard_completion_byte
5156            .zip(context.callable_guard_completion_byte)
5157            .map(|(local, outer)| local.max(outer));
5158        fact.conditional_family = fact.conditional_family.or(context.family);
5159        fact.exhaustive_conditional_family = fact
5160            .exhaustive_conditional_family
5161            .or(context.exhaustive_family);
5162        if let Some(alias) = fact.file_scope_alias.as_mut() {
5163            alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5164                (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5165                (Some(outer), None) => Some(outer.clone()),
5166                (None, local) => local,
5167            };
5168        }
5169        if !context.outer_lexical_scope.is_empty() {
5170            let mut lexical = context.outer_lexical_scope.clone();
5171            lexical.append(&mut fact.lexical_path);
5172            fact.lexical_path = lexical;
5173        }
5174    }
5175
5176    fn recovery_ancestry<'tree>(
5177        &self,
5178        root: Node<'tree>,
5179    ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5180        crate::source_dependencies::RecoveryAncestry::new(
5181            root,
5182            self.source,
5183            self.source_dependencies,
5184        )
5185    }
5186
5187    fn capture_source_dependencies(
5188        &self,
5189        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5190        node: Node<'_>,
5191        unit: &CodeUnit,
5192    ) {
5193        if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5194            self.source_dependencies.borrow_mut().register_primary(
5195                fact.declaration,
5196                node,
5197                unit.is_class(),
5198            );
5199        } else {
5200            self.source_dependencies
5201                .borrow()
5202                .apply_recovered(node, unit.is_class(), fact);
5203        }
5204    }
5205
5206    fn link_source_declaration(
5207        &mut self,
5208        ancestry: &ParentIndex<'_>,
5209        unit: &CodeUnit,
5210        node: Node<'_>,
5211    ) {
5212        let (declaration, family) = {
5213            let mut collector = self.source_collector.borrow_mut();
5214            let occurrence = if node.id() == self.primary_root_id
5215                || self.primary_ancestry.contains(node)
5216            {
5217                collector.intern_node(node)
5218            } else {
5219                // A region reparse owns distinct provenance even when its byte range
5220                // equals a primary node or a previously dropped recovery tree.
5221                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5222            };
5223            let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5224                if self.primary_ancestry.contains(name) {
5225                    collector.intern_node(name)
5226                } else {
5227                    collector
5228                        .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5229                }
5230            });
5231            let declaration = collector.declare(occurrence, name);
5232            let family =
5233                crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5234                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5235                    {
5236                        collector.intern_node(family)
5237                    } else {
5238                        collector.intern_embedded(
5239                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5240                        )
5241                    }
5242                });
5243            (declaration, family)
5244        };
5245        self.source_declarations.insert(declaration);
5246        if !self.source_properties.borrow().contains_key(&declaration) {
5247            let context = crate::source_context::capture_declaration_context(
5248                node,
5249                unit,
5250                self.source,
5251                &mut self.preprocessor_context_cache,
5252            );
5253            let (exhaustive_family, displaced_namespace_closing_brace) = {
5254                let mut collector = self.source_collector.borrow_mut();
5255                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5256                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5257                    {
5258                        collector.intern_node(family)
5259                    } else {
5260                        collector.intern_embedded(
5261                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5262                        )
5263                    }
5264                });
5265                let displaced_namespace_closing_brace =
5266                    context.displaced_namespace_closing_brace.map(|brace| {
5267                        if brace.id() == self.primary_root_id
5268                            || self.primary_ancestry.contains(brace)
5269                        {
5270                            collector.intern_node(brace)
5271                        } else {
5272                            collector.intern_embedded(
5273                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5274                            )
5275                        }
5276                    });
5277                (exhaustive_family, displaced_namespace_closing_brace)
5278            };
5279            let guard_requirements = context.guard_requirements;
5280            let callable_guards = context.callable_guards;
5281            let callable_activation = context.callable_activation;
5282            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5283            let flattened_macro_namespace = context.flattened_macro_namespace;
5284            self.source_properties
5285                .borrow_mut()
5286                .entry(declaration)
5287                .or_insert_with(|| {
5288                    let mut fact = crate::source_facts::capture_declaration(
5289                        declaration,
5290                        node,
5291                        unit,
5292                        self.source,
5293                        ancestry,
5294                    );
5295                    self.capture_source_dependencies(&mut fact, node, unit);
5296                    fact.occurrence_role =
5297                        crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5298                    fact.conditional_family = family;
5299                    fact.guard_requirements = guard_requirements;
5300                    fact.callable_guards = callable_guards;
5301                    fact.callable_activation = callable_activation;
5302                    fact.callable_guard_completion_byte = callable_guard_completion_byte;
5303                    fact.exhaustive_conditional_family = exhaustive_family;
5304                    fact.flattened_macro_namespace = flattened_macro_namespace;
5305                    fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5306                    self.apply_active_source_context(&mut fact);
5307                    fact
5308                });
5309        }
5310        let links = self.source_links.entry(unit.clone()).or_default();
5311        if !links.contains(&declaration) {
5312            links.push(declaration);
5313        }
5314    }
5315
5316    fn link_source_range(
5317        &mut self,
5318        ancestry: &ParentIndex<'_>,
5319        unit: &CodeUnit,
5320        range: Range,
5321        evidence: Option<Node<'_>>,
5322    ) {
5323        let declaration = {
5324            let mut collector = self.source_collector.borrow_mut();
5325            let occurrence =
5326                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5327            let name = evidence
5328                .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5329                .map(|name| {
5330                    if self.primary_ancestry.contains(name) {
5331                        collector.intern_node(name)
5332                    } else {
5333                        collector.intern_embedded(
5334                            brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5335                        )
5336                    }
5337                });
5338            collector.declare(occurrence, name)
5339        };
5340        self.link_source_declaration_facts(
5341            unit,
5342            declaration,
5343            evidence.map(|node| (ancestry, node)),
5344        );
5345    }
5346
5347    /// [`Self::link_source_range`] with the name occurrence interned from the
5348    /// subspan that writes it instead of derived from an evidence node. A live
5349    /// evidence node still supplies the declaration's source facts.
5350    fn link_source_name_range(
5351        &mut self,
5352        unit: &CodeUnit,
5353        range: Range,
5354        name_range: Range,
5355        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5356    ) {
5357        let declaration = {
5358            let mut collector = self.source_collector.borrow_mut();
5359            let occurrence =
5360                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5361            let name =
5362                collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5363            collector.declare(occurrence, Some(name))
5364        };
5365        self.link_source_declaration_facts(unit, declaration, evidence);
5366    }
5367
5368    /// Record `declaration` for `unit` and, when a live evidence node exists,
5369    /// the source facts it supplies: conditional family, guards, context and
5370    /// dependency names. Shared by both occurrence-naming paths so a name
5371    /// subspan never costs a declaration its facts (#3093).
5372    fn link_source_declaration_facts(
5373        &mut self,
5374        unit: &CodeUnit,
5375        declaration: SourceDeclarationId,
5376        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5377    ) {
5378        let family = evidence.and_then(|(_, node)| {
5379            let mut collector = self.source_collector.borrow_mut();
5380            crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5381                if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5382                    collector.intern_node(family)
5383                } else {
5384                    collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5385                        family,
5386                    ))
5387                }
5388            })
5389        });
5390        self.source_declarations.insert(declaration);
5391        if let Some((ancestry, node)) = evidence
5392            && !self.source_properties.borrow().contains_key(&declaration)
5393        {
5394            let context = crate::source_context::capture_declaration_context(
5395                node,
5396                unit,
5397                self.source,
5398                &mut self.preprocessor_context_cache,
5399            );
5400            let (exhaustive_family, displaced_namespace_closing_brace) = {
5401                let mut collector = self.source_collector.borrow_mut();
5402                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5403                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5404                    {
5405                        collector.intern_node(family)
5406                    } else {
5407                        collector.intern_embedded(
5408                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5409                        )
5410                    }
5411                });
5412                let displaced_namespace_closing_brace =
5413                    context.displaced_namespace_closing_brace.map(|brace| {
5414                        if brace.id() == self.primary_root_id
5415                            || self.primary_ancestry.contains(brace)
5416                        {
5417                            collector.intern_node(brace)
5418                        } else {
5419                            collector.intern_embedded(
5420                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5421                            )
5422                        }
5423                    });
5424                (exhaustive_family, displaced_namespace_closing_brace)
5425            };
5426            let guard_requirements = context.guard_requirements;
5427            let callable_guards = context.callable_guards;
5428            let callable_activation = context.callable_activation;
5429            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5430            let flattened_macro_namespace = context.flattened_macro_namespace;
5431            let mut fact = crate::source_facts::capture_declaration(
5432                declaration,
5433                node,
5434                unit,
5435                self.source,
5436                ancestry,
5437            );
5438            self.capture_source_dependencies(&mut fact, node, unit);
5439            fact.occurrence_role =
5440                crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5441            fact.conditional_family = family;
5442            fact.guard_requirements = guard_requirements;
5443            fact.callable_guards = callable_guards;
5444            fact.callable_activation = callable_activation;
5445            fact.callable_guard_completion_byte = callable_guard_completion_byte;
5446            fact.exhaustive_conditional_family = exhaustive_family;
5447            fact.flattened_macro_namespace = flattened_macro_namespace;
5448            fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5449            self.apply_active_source_context(&mut fact);
5450            self.source_properties
5451                .borrow_mut()
5452                .insert(declaration, fact);
5453        }
5454        let links = self.source_links.entry(unit.clone()).or_default();
5455        if !links.contains(&declaration) {
5456            links.push(declaration);
5457        }
5458    }
5459
5460    fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5461        let declaration = *self
5462            .source_links
5463            .get(&unit)
5464            .and_then(|links| links.last())
5465            .expect("signature metadata follows its source declaration");
5466        let metadata_ordinal = self
5467            .parsed
5468            .add_signature_with_metadata(unit.clone(), metadata);
5469        let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5470            declaration,
5471            unit,
5472            metadata_ordinal,
5473        };
5474        if !self.parsed.source_declaration_metadata.contains(&link) {
5475            self.parsed.source_declaration_metadata.push(link);
5476        }
5477    }
5478
5479    /// Records `code_unit` with the answers the walk carries forward, then adds
5480    /// it to the parse product.
5481    ///
5482    /// Every declaration this walk publishes goes through this family, so the
5483    /// carried-forward answers see each one exactly once: the minted declaration
5484    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
5485    fn add_declaration(
5486        &mut self,
5487        ancestry: &ParentIndex<'_>,
5488        code_unit: CodeUnit,
5489        node: Node<'_>,
5490        parent: Option<CodeUnit>,
5491        top_level: Option<CodeUnit>,
5492    ) {
5493        self.note_declaration(&code_unit);
5494        self.link_source_declaration(ancestry, &code_unit, node);
5495        let source = self.source;
5496        self.parsed
5497            .add_code_unit(code_unit, node, source, parent, top_level);
5498    }
5499
5500    /// Range-based form of [`Self::add_declaration`].
5501    fn add_declaration_with_range(
5502        &mut self,
5503        ancestry: &ParentIndex<'_>,
5504        code_unit: CodeUnit,
5505        range: Range,
5506        evidence: Option<Node<'_>>,
5507        parent: Option<CodeUnit>,
5508        top_level: Option<CodeUnit>,
5509    ) {
5510        self.note_declaration(&code_unit);
5511        self.link_source_range(ancestry, &code_unit, range, evidence);
5512        self.parsed
5513            .add_code_unit_with_range(code_unit, range, parent, top_level);
5514    }
5515
5516    /// Name-subspan form of [`Self::add_declaration_with_range`]: the name
5517    /// occurrence is the recorded subspan rather than a token derived from an
5518    /// evidence node (#4292). A live `evidence` node, when the caller has one,
5519    /// still supplies the declaration's source facts; an index-recovered
5520    /// declaration has none.
5521    fn add_declaration_with_name_range(
5522        &mut self,
5523        code_unit: CodeUnit,
5524        range: Range,
5525        name_range: Range,
5526        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5527        parent: Option<CodeUnit>,
5528        top_level: Option<CodeUnit>,
5529    ) {
5530        self.note_declaration(&code_unit);
5531        self.link_source_name_range(&code_unit, range, name_range, evidence);
5532        self.parsed
5533            .add_code_unit_with_range(code_unit, range, parent, top_level);
5534    }
5535
5536    /// Deferred-replacement form of [`Self::add_declaration`].
5537    fn replace_declaration_deferred(
5538        &mut self,
5539        ancestry: &ParentIndex<'_>,
5540        code_unit: CodeUnit,
5541        node: Node<'_>,
5542        parent: Option<CodeUnit>,
5543        top_level: Option<CodeUnit>,
5544    ) {
5545        self.note_replaced_declaration(&code_unit);
5546        self.link_source_declaration(ancestry, &code_unit, node);
5547        let source = self.source;
5548        self.parsed
5549            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5550    }
5551
5552    /// Range-based form of [`Self::replace_declaration_deferred`].
5553    fn replace_declaration_with_range_deferred(
5554        &mut self,
5555        ancestry: &ParentIndex<'_>,
5556        code_unit: CodeUnit,
5557        range: Range,
5558        evidence: Option<Node<'_>>,
5559        parent: Option<CodeUnit>,
5560        top_level: Option<CodeUnit>,
5561    ) {
5562        self.note_replaced_declaration(&code_unit);
5563        self.link_source_range(ancestry, &code_unit, range, evidence);
5564        self.parsed
5565            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5566    }
5567
5568    /// Notes one declaration about to enter the parse product.
5569    ///
5570    /// A declaration the product already holds is not a creation, so an open
5571    /// recovery capture ignores it -- which is the membership test the set
5572    /// difference it replaces performed. A creation inside a nested recovery
5573    /// belongs to the recoveries around it too, so every open capture takes it.
5574    fn note_declaration(&mut self, code_unit: &CodeUnit) {
5575        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5576            for capture in &mut self.recovery_captures {
5577                if capture.removed_pre_existing.contains(code_unit) {
5578                    continue;
5579                }
5580                if capture.created_units.insert(code_unit.clone()) {
5581                    capture.created.push(code_unit.clone());
5582                }
5583            }
5584        }
5585    }
5586
5587    /// Notes one declaration about to replace an existing one.
5588    ///
5589    /// A deferred replacement of a declaration that already owns children
5590    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
5591    /// their source mounts and recovery captures must track the removal.
5592    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5593        // Forward and definition occurrences remain real navigation alternatives.
5594        // Only identities removed with replaced children lose their mounts.
5595        for removed in self.declarations_a_replacement_removes(code_unit) {
5596            self.source_links.remove(&removed);
5597        }
5598        let removes_children = self.parsed.contains_declaration(code_unit)
5599            && self
5600                .parsed
5601                .children
5602                .get(code_unit)
5603                .is_some_and(|children| !children.is_empty());
5604        if removes_children && !self.recovery_captures.is_empty() {
5605            let removed = self.declarations_a_replacement_removes(code_unit);
5606            for capture in &mut self.recovery_captures {
5607                for unit in &removed {
5608                    // A declaration this capture watched being created is
5609                    // its own; one it did not is a declaration that was
5610                    // already there when the capture opened, so creating it
5611                    // again is a restoration and not a mint.
5612                    if !capture.created_units.contains(unit) {
5613                        capture.removed_pre_existing.insert(unit.clone());
5614                    }
5615                }
5616            }
5617        }
5618        self.note_declaration(code_unit);
5619    }
5620
5621    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
5622    /// when `code_unit` is replaced: its children, transitively.
5623    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5624        let mut removed = Vec::new();
5625        let mut seen = HashSet::default();
5626        let mut pending: Vec<CodeUnit> = self
5627            .parsed
5628            .children
5629            .get(code_unit)
5630            .cloned()
5631            .unwrap_or_default();
5632        while let Some(unit) = pending.pop() {
5633            if !seen.insert(unit.clone()) {
5634                continue;
5635            }
5636            if let Some(children) = self.parsed.children.get(&unit) {
5637                pending.extend(children.iter().cloned());
5638            }
5639            removed.push(unit);
5640        }
5641        removed
5642    }
5643
5644    fn visit_recovered_function_like_export_class<'tree>(
5645        &mut self,
5646        node: Node<'tree>,
5647        recovered: RecoveredFunctionLikeExportClass,
5648        scope: &ScopeInfo,
5649        stack: &mut Vec<CppWork<'tree>>,
5650        ancestry: &ParentIndex<'tree>,
5651    ) {
5652        let RecoveredFunctionLikeExportClass {
5653            name,
5654            range,
5655            raw_supertypes,
5656            base_facts,
5657            member_outcome,
5658        } = recovered;
5659        let class_unit = self.visit_named_class_like_shape(
5660            node,
5661            name,
5662            None,
5663            true,
5664            Some(range),
5665            raw_supertypes,
5666            base_facts,
5667            scope,
5668            stack,
5669            ancestry,
5670        );
5671        self.parsed
5672            .record_materialization(MaterializationRecord::RecoveredDeclaration {
5673                recovery: range,
5674                unit: class_unit.clone(),
5675            });
5676        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5677            && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5678                tree.root_node(),
5679                class_unit.identifier(),
5680                self.source,
5681            )
5682        {
5683            self.visit_recovered_fragment_constructor(
5684                constructor_range,
5685                body,
5686                node,
5687                &class_unit,
5688                scope,
5689                ancestry,
5690            );
5691        }
5692        if let Some(outcome) = member_outcome {
5693            self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5694        }
5695    }
5696
5697    fn visit_function_like_export_class_pair<'tree>(
5698        &mut self,
5699        node: Node<'tree>,
5700        scope: &ScopeInfo,
5701        stack: &mut Vec<CppWork<'tree>>,
5702        ancestry: &ParentIndex<'tree>,
5703    ) -> bool {
5704        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5705            return false;
5706        };
5707        let member_outcome = self
5708            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5709        // A malformed class body can escape into several following siblings
5710        // before the next export-macro class head appears. Inspect siblings in
5711        // order and stop at the first envelope that contains such a head. One
5712        // envelope can contain several following classes, all recovered in a
5713        // single bounded traversal.
5714        let mut displaced = node.next_named_sibling();
5715        while let Some(candidate) = displaced {
5716            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5717                break;
5718            }
5719            displaced = candidate.next_named_sibling();
5720        }
5721        // The adjacent initializer_list proves the class body envelope, but
5722        // its children are expression-shaped rather than declaration-shaped.
5723        // Visit only separately recovered members; ordinary callable
5724        // definitions remain available from their out-of-line declarations.
5725        let range = recovered.range;
5726        self.visit_recovered_function_like_export_class(
5727            node,
5728            RecoveredFunctionLikeExportClass {
5729                name: recovered.name,
5730                range,
5731                raw_supertypes: recovered.raw_supertypes,
5732                base_facts: recovered.base_facts,
5733                member_outcome,
5734            },
5735            scope,
5736            stack,
5737            ancestry,
5738        );
5739        self.consumed_fragment_regions
5740            .push((node.start_byte(), range.end_byte));
5741        true
5742    }
5743
5744    fn visit_embedded_function_like_export_classes<'tree>(
5745        &mut self,
5746        node: Node<'tree>,
5747        scope: &ScopeInfo,
5748        stack: &mut Vec<CppWork<'tree>>,
5749        ancestry: &ParentIndex<'tree>,
5750    ) -> bool {
5751        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5752        let found = !recovered_classes.is_empty();
5753        for recovered in recovered_classes {
5754            let member_outcome = self.reparse_fragmented_export_class_members(
5755                &recovered.fragmented_body,
5756                &recovered.name,
5757            );
5758            self.visit_recovered_function_like_export_class(
5759                node,
5760                RecoveredFunctionLikeExportClass {
5761                    name: recovered.name,
5762                    range: recovered.range,
5763                    raw_supertypes: Some(recovered.raw_supertypes),
5764                    base_facts: Some(recovered.base_facts),
5765                    member_outcome,
5766                },
5767                scope,
5768                stack,
5769                ancestry,
5770            );
5771        }
5772        found
5773    }
5774
5775    /// Walk `node`'s container, answering every ancestor question from
5776    /// `ancestry`.
5777    ///
5778    /// `ancestry` must index the tree `node` belongs to. The caller owns it
5779    /// because one tree can be walked more than once -- a header's C and C++
5780    /// readings are the same tree under different tag semantics -- and the
5781    /// parent relation is a property of the tree, not of the reading.
5782    #[allow(clippy::too_many_arguments)]
5783    pub fn visit_container<'tree>(
5784        &mut self,
5785        node: Node<'tree>,
5786        ancestry: &ParentIndex<'tree>,
5787        package_name: &str,
5788        module: Option<CodeUnit>,
5789        class_unit: Option<CodeUnit>,
5790        template_signature: Option<String>,
5791        visible_using_namespaces: Vec<String>,
5792        visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
5793    ) {
5794        let scope = ScopeInfo {
5795            package_name: package_name.to_string(),
5796            module,
5797            class_unit,
5798            template_signature,
5799            template_metadata: None,
5800            declarations_are_fields: false,
5801            recovered_specialization_member_scope: false,
5802            visible_using_namespaces,
5803            local_block: None,
5804            visible_using_declarations,
5805        };
5806        // The work loop dispatches a container's children, never the container
5807        // itself, so an `ERROR` container that is the seed does not reach the
5808        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
5809        // breaks the parse leaves the whole translation unit as that seed
5810        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
5811        // macro-error-class recovery reads.
5812        if node.is_error() {
5813            self.visit_object_macro_error_classes(node, &scope);
5814        }
5815        self.run_container_work(node, scope, ancestry);
5816        self.drain_partitioned_regions();
5817    }
5818
5819    /// Drain reparsed containers after their primary walk has indexed them.
5820    /// Recovered nodes retain their own tree and source context while they are
5821    /// visited; only then are their byte ranges marked consumed so a shared
5822    /// primary event stream does not visit the same declarations twice.
5823    fn drain_partitioned_regions(&mut self) {
5824        let mut consumed = Vec::new();
5825        while let Some(CppPartitionedRegion {
5826            tree,
5827            range,
5828            scope,
5829            recovery,
5830            source_context,
5831        }) = self.partitioned_regions.pop()
5832        {
5833            let root = tree.root_node();
5834            let container = root
5835                .descendant_for_byte_range(range.start, range.end)
5836                .expect("the queued container belongs to this tree");
5837            assert_eq!(container.byte_range(), range);
5838            let ancestry = self.recovery_ancestry(root);
5839            self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5840                visitor.run_container_work(container, scope, &ancestry)
5841            });
5842            // A recovered walk may enqueue another partition inside this
5843            // region. Delay the outer claim until all nested regions have
5844            // drained, or their nodes would be skipped as already consumed.
5845            consumed.push((range.start, range.end));
5846        }
5847        self.consumed_fragment_regions.extend(consumed);
5848    }
5849
5850    /// Whether a work node lies entirely inside a byte region consumed by a
5851    /// fragmented export-class recovery (#938); such nodes were already indexed
5852    /// as members of the recovered class by the region reparse.
5853    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5854        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5855    }
5856
5857    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
5858    /// candidate a recovery is about to reparse but has not yet turned into a
5859    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
5860    /// prior structured recovery.
5861    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5862        self.consumed_fragment_regions
5863            .iter()
5864            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5865    }
5866
5867    /// Drive the container work loop from an explicit seed scope to completion. The
5868    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
5869    /// stays alive for the whole traversal.
5870    ///
5871    /// Every ancestor question this walk asks is answered from `ancestry`, which
5872    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
5873    /// node's position in the tree, which made a generated header with thousands
5874    /// of top-level declarations quadratic (#2361). The index is the caller's
5875    /// because it outlives any one walk: the file's tree is walked twice when a
5876    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
5877    /// builds its own index for its own tree.
5878    fn run_container_work<'tree>(
5879        &mut self,
5880        node: Node<'tree>,
5881        scope: ScopeInfo,
5882        ancestry: &ParentIndex<'tree>,
5883    ) {
5884        self.drain_cpp_work(
5885            vec![CppWork::Container(CppContainer { node, scope })],
5886            ancestry,
5887        );
5888    }
5889
5890    /// The work loop itself, from whatever seed the caller built.
5891    ///
5892    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
5893    /// that must walk only part of a reparsed tree seeds it with the sibling
5894    /// range it may walk instead, which keeps the `using namespace X;` scope
5895    /// accumulation `advance_cpp_siblings` performs.
5896    fn drain_cpp_work<'tree>(
5897        &mut self,
5898        mut stack: Vec<CppWork<'tree>>,
5899        ancestry: &ParentIndex<'tree>,
5900    ) {
5901        while let Some(work) = stack.pop() {
5902            match work {
5903                CppWork::Container(container) => {
5904                    push_cpp_container_work(container.node, container.scope, &mut stack);
5905                }
5906                CppWork::Siblings(siblings) => {
5907                    advance_cpp_siblings(siblings, self.source, &mut stack);
5908                }
5909                CppWork::Node(work) => {
5910                    if self.node_is_inside_consumed_fragment(work.node) {
5911                        continue;
5912                    }
5913                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5914                }
5915            }
5916        }
5917    }
5918
5919    /// Drain nodes selected by a bounded recovery walk without filtering them
5920    /// against consumed regions from the primary tree.
5921    fn drain_cpp_recovery_work<'tree>(
5922        &mut self,
5923        mut stack: Vec<CppWork<'tree>>,
5924        ancestry: &ParentIndex<'tree>,
5925    ) {
5926        while let Some(work) = stack.pop() {
5927            match work {
5928                CppWork::Container(container) => {
5929                    push_cpp_container_work(container.node, container.scope, &mut stack);
5930                }
5931                CppWork::Siblings(siblings) => {
5932                    advance_cpp_siblings(siblings, self.source, &mut stack);
5933                }
5934                CppWork::Node(work) => {
5935                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5936                }
5937            }
5938        }
5939    }
5940
5941    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
5942    /// it only when the entire region is member-shaped. This validation must happen
5943    /// before registering the recovered class because a rejected speculative range
5944    /// must not leak into the ordinary recovery path.
5945    fn reparse_fragmented_export_class_members(
5946        &self,
5947        fragmented: &FragmentedExportBody,
5948        class_name: &str,
5949    ) -> Option<FragmentedExportMembers> {
5950        if fragmented.reparse_start >= fragmented.reparse_end {
5951            return None;
5952        }
5953        let tree = cpp_reparse_fragmented_class_body(
5954            self.source,
5955            fragmented.reparse_start,
5956            fragmented.reparse_end,
5957        )?;
5958        if cpp_reparsed_members_are_indexable_for_class(
5959            tree.root_node(),
5960            self.source,
5961            Some(class_name),
5962        ) {
5963            return Some(FragmentedExportMembers::Complete(tree));
5964        }
5965        let has_conditional_constructor = {
5966            let root = tree.root_node();
5967            let mut cursor = root.walk();
5968            root.named_children(&mut cursor).any(|child| {
5969                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5970            })
5971        };
5972        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5973    }
5974
5975    /// Index an already validated fragmented body as members of `class_unit`. The
5976    /// region reparse keeps each member's exact original byte and line positions.
5977    fn visit_fragmented_export_class_members(
5978        &mut self,
5979        origin: Node<'_>,
5980        outcome: FragmentedExportMembers,
5981        class_unit: CodeUnit,
5982        scope: &ScopeInfo,
5983    ) -> bool {
5984        self.with_recovery_source_context(origin, |visitor| {
5985            visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5986        })
5987    }
5988
5989    fn visit_fragmented_export_class_members_in_context(
5990        &mut self,
5991        outcome: FragmentedExportMembers,
5992        class_unit: CodeUnit,
5993        scope: &ScopeInfo,
5994    ) -> bool {
5995        let (tree, complete) = match outcome {
5996            FragmentedExportMembers::Complete(tree) => (tree, true),
5997            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5998        };
5999        let root = tree.root_node();
6000        let class_name = class_unit.identifier().to_string();
6001        let member_scope = ScopeInfo {
6002            // A recovered export-macro class may borrow its namespace from an
6003            // earlier forward declaration even when the malformed node itself
6004            // sits at file scope. Use the recovered class identity as the
6005            // authoritative package for reparsed members as well.
6006            package_name: class_unit.package_name().to_string(),
6007            module: scope.module.clone(),
6008            class_unit: Some(class_unit),
6009            template_signature: scope.template_signature.clone(),
6010            template_metadata: None,
6011            declarations_are_fields: true,
6012            recovered_specialization_member_scope: false,
6013            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6014            local_block: None,
6015            visible_using_declarations: scope.visible_using_declarations.clone(),
6016        };
6017        let reparsed_ancestry = self.recovery_ancestry(root);
6018        if !complete {
6019            // A conditional beginning immediately after an access label can
6020            // fragment one constructor declaration while leaving the rest of
6021            // the class body as unsafe statement soup. Recover only that
6022            // structurally proven constructor and leave the outer-tree
6023            // siblings unconsumed for their ordinary walk.
6024            let mut cursor = root.walk();
6025            let constructors = root
6026                .named_children(&mut cursor)
6027                .filter_map(|child| {
6028                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
6029                })
6030                .collect::<Vec<_>>();
6031            // The reparsed region is its own tree, so this drain walks it with
6032            // its own parent index.
6033            for constructor in constructors {
6034                let mut stack = Vec::new();
6035                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
6036                self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
6037            }
6038            return false;
6039        }
6040        let mut cursor = root.walk();
6041        let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6042        for member in members.iter().copied().filter(|member| {
6043            cpp_reparsed_defaulted_constructor_is_indexable(
6044                *member,
6045                &members,
6046                Some(class_name.as_str()),
6047                self.source,
6048            )
6049        }) {
6050            self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6051        }
6052        for member in members.iter().copied().filter(|member| {
6053            cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6054        }) {
6055            self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6056        }
6057        // The reparsed region is its own tree, so this walk indexes it itself.
6058        self.run_container_work(root, member_scope, &reparsed_ancestry);
6059        true
6060    }
6061
6062    fn visit_recovered_fragment_constructor<'tree>(
6063        &mut self,
6064        range: std::ops::Range<usize>,
6065        constructor_body: Node<'tree>,
6066        class_declaration: Node<'tree>,
6067        class_unit: &CodeUnit,
6068        scope: &ScopeInfo,
6069        ancestry: &ParentIndex<'tree>,
6070    ) {
6071        self.with_recovery_source_context(class_declaration, |visitor| {
6072            visitor.visit_recovered_fragment_constructor_in_context(
6073                range,
6074                constructor_body,
6075                class_declaration,
6076                class_unit,
6077                scope,
6078                ancestry,
6079            );
6080        });
6081    }
6082
6083    fn visit_recovered_fragment_constructor_in_context<'tree>(
6084        &mut self,
6085        range: std::ops::Range<usize>,
6086        constructor_body: Node<'tree>,
6087        class_declaration: Node<'tree>,
6088        class_unit: &CodeUnit,
6089        scope: &ScopeInfo,
6090        ancestry: &ParentIndex<'tree>,
6091    ) {
6092        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6093            return;
6094        };
6095        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6096            tree.root_node(),
6097            range.start,
6098            class_unit.identifier(),
6099            self.source,
6100        ) else {
6101            return;
6102        };
6103        let member_scope = ScopeInfo {
6104            package_name: class_unit.package_name().to_string(),
6105            module: scope.module.clone(),
6106            class_unit: Some(class_unit.clone()),
6107            template_signature: scope.template_signature.clone(),
6108            template_metadata: None,
6109            declarations_are_fields: true,
6110            recovered_specialization_member_scope: false,
6111            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6112            local_block: None,
6113            visible_using_declarations: scope.visible_using_declarations.clone(),
6114        };
6115        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6116        else {
6117            return;
6118        };
6119        debug_assert_eq!(function.name, class_unit.identifier());
6120        let code_unit = function.code_unit(self.file.clone());
6121        let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6122        self.add_declaration_with_range(
6123            &recovered_ancestry,
6124            code_unit.clone(),
6125            Range {
6126                start_byte: function_declarator.start_byte(),
6127                end_byte: constructor_body.end_byte(),
6128                start_line: function_declarator.start_position().row + 1,
6129                end_line: constructor_body.end_position().row + 1,
6130            },
6131            Some(function_declarator),
6132            None,
6133            None,
6134        );
6135        let declaration = *self.source_links[&code_unit]
6136            .last()
6137            .expect("recovered constructor source");
6138        {
6139            let mut properties = self.source_properties.borrow_mut();
6140            let fact = properties
6141                .get_mut(&declaration)
6142                .expect("recovered constructor source fact");
6143            fact.occurrence_role =
6144                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6145            fact.callable_is_constructor = true;
6146        }
6147        self.add_signature_with_metadata(
6148            code_unit.clone(),
6149            cpp_signature_metadata(
6150                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6151                function_declarator,
6152                cpp_callable_linkage(class_declaration, self.source, ancestry),
6153                self.source,
6154                &recovered_ancestry,
6155            )
6156            .with_declaration_only(false),
6157        );
6158        self.parsed.add_child(class_unit.clone(), code_unit);
6159    }
6160
6161    fn visit_recovered_fragment_prefix_members<'tree>(
6162        &mut self,
6163        origin: Node<'_>,
6164        root: Node<'tree>,
6165        constructor_start: usize,
6166        class_unit: &CodeUnit,
6167        scope: &ScopeInfo,
6168        ancestry: &ParentIndex<'tree>,
6169    ) {
6170        self.with_recovery_source_context(origin, |visitor| {
6171            visitor.visit_recovered_fragment_prefix_members_in_context(
6172                root,
6173                constructor_start,
6174                class_unit,
6175                scope,
6176                ancestry,
6177            );
6178        });
6179    }
6180
6181    fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6182        &mut self,
6183        root: Node<'tree>,
6184        constructor_start: usize,
6185        class_unit: &CodeUnit,
6186        scope: &ScopeInfo,
6187        ancestry: &ParentIndex<'tree>,
6188    ) {
6189        let member_scope = ScopeInfo {
6190            package_name: class_unit.package_name().to_string(),
6191            module: scope.module.clone(),
6192            class_unit: Some(class_unit.clone()),
6193            template_signature: scope.template_signature.clone(),
6194            template_metadata: None,
6195            declarations_are_fields: true,
6196            recovered_specialization_member_scope: false,
6197            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6198            local_block: None,
6199            visible_using_declarations: scope.visible_using_declarations.clone(),
6200        };
6201        let mut root_cursor = root.walk();
6202        let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6203        let mut stack = vec![root];
6204        while let Some(current) = stack.pop() {
6205            if current.kind() == "comment" || current.start_byte() >= constructor_start {
6206                continue;
6207            }
6208            if current
6209                .parent()
6210                .is_some_and(|parent| same_node(parent, root))
6211                && cpp_reparsed_defaulted_constructor_is_indexable(
6212                    current,
6213                    &members,
6214                    Some(class_unit.identifier()),
6215                    self.source,
6216                )
6217            {
6218                self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6219                continue;
6220            }
6221            if current.end_byte() <= constructor_start
6222                && current.kind() != "translation_unit"
6223                && current.kind() != "labeled_statement"
6224                && current.kind() != "ERROR"
6225            {
6226                let mut work_stack = Vec::new();
6227                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6228                self.drain_cpp_recovery_work(work_stack, ancestry);
6229                continue;
6230            }
6231            if matches!(
6232                current.kind(),
6233                "translation_unit" | "labeled_statement" | "ERROR"
6234            ) {
6235                let mut cursor = current.walk();
6236                stack.extend(current.named_children(&mut cursor));
6237            }
6238        }
6239    }
6240
6241    fn visit_recovered_defaulted_constructor(
6242        &mut self,
6243        statement: Node<'_>,
6244        scope: &ScopeInfo,
6245        ancestry: &ParentIndex<'_>,
6246    ) {
6247        let Some(parent) = scope.class_unit.clone() else {
6248            return;
6249        };
6250        let Some(assignment) = statement.named_child(0) else {
6251            return;
6252        };
6253        let Some(call) = assignment.child_by_field_name("left") else {
6254            return;
6255        };
6256        let Some(function_name) = call.child_by_field_name("function") else {
6257            return;
6258        };
6259        let Some(arguments) = call.child_by_field_name("arguments") else {
6260            return;
6261        };
6262        if function_name.kind() != "identifier"
6263            || node_text(function_name, self.source) != parent.identifier()
6264            || arguments.kind() != "argument_list"
6265            || arguments.named_child_count() != 0
6266        {
6267            return;
6268        }
6269        let function = FunctionInfo {
6270            package_name: scope.package_name.clone(),
6271            owner: Some(CppMemberOwner::Unit(parent.clone())),
6272            name: parent.identifier().to_string(),
6273            signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6274        };
6275        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6276        if self.parsed.contains_declaration(&code_unit) {
6277            return;
6278        }
6279        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6280        let metadata = SignatureMetadata::new(
6281            normalize_cpp_whitespace(node_text(statement, self.source)),
6282            Vec::new(),
6283        )
6284        .with_declaration_only(true)
6285        .with_callable_arity(CallableArity::exact(0))
6286        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6287        // The reparse proves class ownership, but carries no original class
6288        // access ladder. Preserve unknown access rather than guessing it.
6289        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6290        self.add_signature_with_metadata(code_unit.clone(), metadata);
6291        if let Some(declaration) = self
6292            .source_links
6293            .get(&code_unit)
6294            .and_then(|links| links.last())
6295            .copied()
6296        {
6297            let mut properties = self.source_properties.borrow_mut();
6298            let fact = properties
6299                .get_mut(&declaration)
6300                .expect("recovered defaulted constructor source fact");
6301            fact.occurrence_role =
6302                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6303            fact.callable_is_constructor = true;
6304        }
6305        self.parsed.add_child(parent, code_unit);
6306    }
6307
6308    /// Recover a constructor declaration a statement-context class-body
6309    /// reparse read as a call or parenthesized variable declarator. The
6310    /// statement is validated and its parameters read from a synthetic class
6311    /// context, where the constructor declaration parses as a real function
6312    /// declarator; the declaration itself is minted from the original
6313    /// statement node, so every range keeps its original byte positions.
6314    fn visit_bare_constructor_expression<'tree>(
6315        &mut self,
6316        statement: Node<'tree>,
6317        scope: &ScopeInfo,
6318        ancestry: &ParentIndex<'tree>,
6319    ) -> bool {
6320        let Some(parent) = scope.class_unit.clone() else {
6321            return false;
6322        };
6323        const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6324        let region = &self.source[statement.start_byte()..statement.end_byte()];
6325        let parsed = crate::graph::syntax::parse_sentinel(
6326            PREFIX,
6327            crate::graph::syntax::SentinelInput::region(region),
6328            " };",
6329        );
6330        let (synthetic, tree) = (parsed.source, parsed.tree);
6331        let root = tree.root_node();
6332        if root.has_error() {
6333            return false;
6334        }
6335        let mut declarator = None;
6336        let mut stack = vec![root];
6337        while let Some(current) = stack.pop() {
6338            if current.kind() == "function_declarator" {
6339                if declarator.is_some() {
6340                    return false;
6341                }
6342                declarator = Some(current);
6343                continue;
6344            }
6345            let mut cursor = current.walk();
6346            stack.extend(current.named_children(&mut cursor));
6347        }
6348        let Some(declarator) = declarator else {
6349            return false;
6350        };
6351        let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6352            return false;
6353        };
6354        let signature = cpp_parameter_signature(parameters_node, &synthetic);
6355        let mut parameter_labels = Vec::new();
6356        let mut parameter_cursor = parameters_node.walk();
6357        for parameter in parameters_node.named_children(&mut parameter_cursor) {
6358            if matches!(
6359                parameter.kind(),
6360                "parameter_declaration" | "optional_parameter_declaration"
6361            ) && let Some(name) = parameter
6362                .child_by_field_name("declarator")
6363                .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6364            {
6365                parameter_labels.push(name);
6366            }
6367        }
6368        let arity = parameter_labels.len();
6369        let function = FunctionInfo {
6370            package_name: scope.package_name.clone(),
6371            owner: Some(CppMemberOwner::Unit(parent.clone())),
6372            name: parent.identifier().to_string(),
6373            signature,
6374        };
6375        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6376        if self.parsed.contains_declaration(&code_unit) {
6377            return true;
6378        }
6379        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6380        let metadata = SignatureMetadata::with_parameter_labels(
6381            normalize_cpp_whitespace(node_text(statement, self.source)),
6382            parameter_labels,
6383        )
6384        .with_declaration_only(true)
6385        .with_callable_arity(CallableArity::exact(arity))
6386        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6387        // The reparse proves class ownership, but carries no original
6388        // class access ladder. Preserve unknown access rather than
6389        // guessing it.
6390        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6391        self.add_signature_with_metadata(code_unit.clone(), metadata);
6392        if let Some(declaration) = self
6393            .source_links
6394            .get(&code_unit)
6395            .and_then(|links| links.last())
6396            .copied()
6397        {
6398            let mut properties = self.source_properties.borrow_mut();
6399            let fact = properties
6400                .get_mut(&declaration)
6401                .expect("recovered constructor source fact");
6402            fact.occurrence_role =
6403                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6404            fact.callable_is_constructor = true;
6405        }
6406        self.parsed.add_child(parent, code_unit);
6407        true
6408    }
6409
6410    /// Capture ancestors only when a proven recovery enters another tree. The
6411    /// ordinary declaration driver never performs this additional ancestor walk.
6412    fn with_recovery_source_context<T>(
6413        &mut self,
6414        node: Node<'_>,
6415        walk: impl FnOnce(&mut Self) -> T,
6416    ) -> T {
6417        self.with_recovery_source_context_at(node, node, walk)
6418    }
6419
6420    /// [`Self::with_recovery_source_context`] for a recovery whose content
6421    /// begins inside `node` at `preprocessor_anchor`: the lexical owners come
6422    /// from `node`, the preprocessor facts from the anchor. The two differ when
6423    /// the malformed node swallowed a branch directive that precedes the
6424    /// recovered content.
6425    fn with_recovery_source_context_at<T>(
6426        &mut self,
6427        node: Node<'_>,
6428        preprocessor_anchor: Node<'_>,
6429        walk: impl FnOnce(&mut Self) -> T,
6430    ) -> T {
6431        use crate::graph::resolver::{
6432            enclosing_namespace_context, merge_preprocessor_guards,
6433            preprocessor_conditional_family_for_declaration_with_cache,
6434            preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6435        };
6436        let previous = self.active_source_context.take();
6437        let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6438        let inherited = (!primary).then_some(previous.as_ref()).flatten();
6439        let local_guards = preprocessor_guard_environment_with_cache(
6440            preprocessor_anchor,
6441            self.source,
6442            &mut self.preprocessor_context_cache,
6443        );
6444        let guards = match inherited {
6445            Some(outer) => outer
6446                .guards
6447                .as_ref()
6448                .zip(local_guards.as_ref())
6449                .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6450            None => local_guards,
6451        };
6452        let local_namespace = enclosing_namespace_context(node, self.source);
6453        let namespace = match (
6454            inherited.and_then(|outer| outer.child_namespace.as_ref()),
6455            local_namespace,
6456        ) {
6457            (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6458            (Some(outer), None) => Some(outer.clone()),
6459            (None, local) => local,
6460        };
6461        let outer_lexical_scope =
6462            inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6463        let mut local_owners = Vec::new();
6464        let mut parent = node.parent();
6465        while let Some(ancestor) = parent {
6466            if matches!(
6467                ancestor.kind(),
6468                "namespace_definition"
6469                    | "class_specifier"
6470                    | "struct_specifier"
6471                    | "union_specifier"
6472                    | "enum_specifier"
6473            ) && let Some(name) = ancestor.child_by_field_name("name")
6474            {
6475                local_owners.push(node_text(name, self.source).to_owned());
6476            }
6477            parent = ancestor.parent();
6478        }
6479        local_owners.reverse();
6480        let mut child_lexical_scope = outer_lexical_scope.clone();
6481        child_lexical_scope.extend(local_owners);
6482        if matches!(
6483            node.kind(),
6484            "namespace_definition"
6485                | "class_specifier"
6486                | "struct_specifier"
6487                | "union_specifier"
6488                | "enum_specifier"
6489        ) && let Some(name) = node.child_by_field_name("name")
6490        {
6491            child_lexical_scope.push(node_text(name, self.source).to_owned());
6492        }
6493        let child_namespace = if node.kind() == "namespace_definition" {
6494            node.child_by_field_name("name")
6495                .map(|name| {
6496                    let local = node_text(name, self.source);
6497                    namespace
6498                        .as_ref()
6499                        .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6500                })
6501                .or_else(|| namespace.clone())
6502        } else {
6503            namespace.clone()
6504        };
6505        let intern = |family: Node<'_>| {
6506            let mut collector = self.source_collector.borrow_mut();
6507            if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6508                collector.intern_node(family)
6509            } else {
6510                collector
6511                    .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6512            }
6513        };
6514        let family = preprocessor_conditional_family_node(preprocessor_anchor)
6515            .map(intern)
6516            .or_else(|| inherited.and_then(|outer| outer.family));
6517        let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6518            preprocessor_anchor,
6519            &mut self.preprocessor_context_cache,
6520        )
6521        .map(intern)
6522        .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6523        let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6524            preprocessor_anchor,
6525            self.source,
6526            &mut self.preprocessor_context_cache,
6527        );
6528        let callable_guard_completion_byte = match inherited {
6529            Some(outer) => local_completion
6530                .zip(outer.callable_guard_completion_byte)
6531                .map(|(local, outer)| local.max(outer)),
6532            None => local_completion,
6533        };
6534        self.active_source_context = Some(CppActiveSourceContext {
6535            guards,
6536            namespace: child_namespace.clone(),
6537            child_namespace,
6538            outer_lexical_scope: child_lexical_scope.clone(),
6539            child_lexical_scope,
6540            family,
6541            exhaustive_family,
6542            callable_guard_completion_byte,
6543        });
6544        let result = walk(self);
6545        self.active_source_context = previous;
6546        result
6547    }
6548
6549    fn visit_node<'tree>(
6550        &mut self,
6551        node: Node<'tree>,
6552        scope: &ScopeInfo,
6553        stack: &mut Vec<CppWork<'tree>>,
6554        ancestry: &ParentIndex<'tree>,
6555    ) {
6556        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6557            self.visit_node(node, &recovered_scope, stack, ancestry);
6558            return;
6559        }
6560        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6561            self.visit_node(node, &recovered_scope, stack, ancestry);
6562            return;
6563        }
6564        if scope.class_unit.is_some()
6565            && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6566        {
6567            self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6568            return;
6569        }
6570        // Fragmented-class recovery below may consume a malformed function
6571        // envelope before the ordinary kind dispatch runs. Recover any
6572        // export-macro class embedded in that envelope first; the strict class
6573        // head/base/body predicate is independent of which later recovery owns
6574        // the surrounding parser fragment.
6575        if node.kind() == "function_definition" && node.has_error() {
6576            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6577        }
6578        if let Some(FragmentedClassRecovery {
6579            declaration_node: class_node,
6580            name,
6581            raw_supertypes,
6582            body: fragmented,
6583        }) = fragmented_class_body(node, self.source)
6584        {
6585            let displaced_namespace_items =
6586                displaced_fragment_namespace_geometry(node, self.source)
6587                    .map(|boundary| boundary.namespace_items)
6588                    .unwrap_or_default();
6589            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6590            let mut class_stack = Vec::new();
6591            // When the full body cannot be safely reparsed, the original class
6592            // node still proves ownership for its parser-visible prefix.
6593            let parser_visible_body =
6594                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6595                    .then(|| cpp_body_node(class_node))
6596                    .flatten();
6597            let class_unit = self.visit_named_class_like_shape(
6598                class_node,
6599                name,
6600                parser_visible_body,
6601                true,
6602                Some(fragmented.class_range),
6603                Some(raw_supertypes),
6604                Some(extract_cpp_base_facts(class_node, self.source)),
6605                scope,
6606                &mut class_stack,
6607                ancestry,
6608            );
6609            let member_scope = ScopeInfo {
6610                package_name: class_unit.package_name().to_string(),
6611                module: scope.module.clone(),
6612                class_unit: Some(class_unit.clone()),
6613                template_signature: scope.template_signature.clone(),
6614                template_metadata: None,
6615                declarations_are_fields: true,
6616                recovered_specialization_member_scope: false,
6617                visible_using_namespaces: scope.visible_using_namespaces.clone(),
6618                local_block: None,
6619                visible_using_declarations: scope.visible_using_declarations.clone(),
6620            };
6621            let complete = outcome.is_some_and(|outcome| {
6622                self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6623            });
6624            if complete {
6625                self.consumed_fragment_regions
6626                    .push((node.start_byte(), fragmented.class_range.end_byte));
6627            } else {
6628                // A macro-constrained member can make the full body reparse
6629                // unsafe while tree-sitter still exposes later class members
6630                // as bounded siblings up to the displaced `}`/`;`. Keep the
6631                // structurally proven class/base declaration and re-own those
6632                // sibling nodes under it. They retain their original parser
6633                // nodes and exact ranges; the close boundary comes solely from
6634                // `fragmented_class_body`.
6635                // Template wrappers put the escaped members beside the
6636                // template rather than beside its malformed declaration.
6637                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6638                    if candidate.start_byte() >= fragmented.reparse_end {
6639                        break;
6640                    }
6641                    if cpp_fragment_sibling_is_class_member(
6642                        candidate,
6643                        fragmented.reparse_end,
6644                        self.source,
6645                    ) {
6646                        self.recovered_class_sibling_scopes
6647                            .insert(candidate.id(), member_scope.clone());
6648                    }
6649                }
6650                // The macro-declaration grammar keeps the class head and its
6651                // parser-visible prefix members inside the fragmented
6652                // container itself, where there is no separate body node for
6653                // `visit_named_class_like_shape` to seed. Walk the container
6654                // with the member scope so those prefix members are owned by
6655                // the class; the head's bare tokens mint nothing on their own.
6656                if class_node.kind() == "ERROR" && class_node.has_error() {
6657                    stack.push(CppWork::Container(CppContainer {
6658                        node: class_node,
6659                        scope: member_scope,
6660                    }));
6661                }
6662            }
6663            for item in displaced_namespace_items {
6664                self.recovered_class_sibling_scopes
6665                    .insert(item.id(), scope.clone());
6666            }
6667            stack.extend(class_stack);
6668            return;
6669        }
6670        if self.visit_folded_aggregate(node, scope) {
6671            return;
6672        }
6673        match node.kind() {
6674            "template_declaration" => {
6675                if let Some(recovered) =
6676                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
6677                {
6678                    let mut template_scope = scope.clone();
6679                    template_scope.template_signature =
6680                        cpp_template_signature(node, recovered.declaration_node, self.source);
6681                    template_scope.template_metadata =
6682                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6683                    let raw_supertypes =
6684                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
6685                    let mut class_stack = Vec::new();
6686                    let class_unit = self.visit_named_class_like_shape(
6687                        recovered.class_node,
6688                        recovered.name,
6689                        Some(recovered.body),
6690                        true,
6691                        Some(recovered.range),
6692                        raw_supertypes,
6693                        Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6694                        &template_scope,
6695                        &mut class_stack,
6696                        ancestry,
6697                    );
6698                    self.parsed.record_materialization(
6699                        MaterializationRecord::RecoveredDeclaration {
6700                            recovery: recovered.range,
6701                            unit: class_unit.clone(),
6702                        },
6703                    );
6704                    let member_scope = ScopeInfo {
6705                        package_name: template_scope.package_name.clone(),
6706                        module: template_scope.module.clone(),
6707                        class_unit: Some(class_unit.clone()),
6708                        template_signature: template_scope.template_signature.clone(),
6709                        template_metadata: None,
6710                        declarations_are_fields: true,
6711                        recovered_specialization_member_scope: recovered
6712                            .class_node
6713                            .child_by_field_name("name")
6714                            .is_some_and(|name| name.kind() == "template_type"),
6715                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6716                        local_block: None,
6717                        visible_using_declarations: template_scope
6718                            .visible_using_declarations
6719                            .clone(),
6720                    };
6721                    for tail_member in recovered.tail_members.into_iter().rev() {
6722                        stack.push(CppWork::Node(CppNodeWork {
6723                            node: tail_member,
6724                            scope: member_scope.clone(),
6725                        }));
6726                    }
6727                    stack.extend(class_stack);
6728                    for sibling in recovered.member_siblings {
6729                        self.recovered_class_sibling_scopes
6730                            .insert(sibling.id(), member_scope.clone());
6731                    }
6732                    return;
6733                }
6734                for index in (0..node.named_child_count()).rev() {
6735                    let Some(child) = node.named_child(index) else {
6736                        continue;
6737                    };
6738                    if matches!(
6739                        child.kind(),
6740                        "class_specifier"
6741                            | "struct_specifier"
6742                            | "union_specifier"
6743                            | "enum_specifier"
6744                            | "function_definition"
6745                            | "declaration"
6746                            | "field_declaration"
6747                            | "alias_declaration"
6748                            | "namespace_definition"
6749                            // `template <class T> template <class U> void
6750                            // Box<T>::take(U) {}`: an out-of-line member
6751                            // template nests one head inside another. The
6752                            // inner head is the member's own and replaces the
6753                            // signature head when the nested node is visited.
6754                            | "template_declaration"
6755                    ) {
6756                        let mut template_scope = scope.clone();
6757                        template_scope.template_signature =
6758                            cpp_template_signature(node, child, self.source);
6759                        template_scope.template_metadata =
6760                            cpp_template_metadata(node, child, self.source, ancestry);
6761                        if let Some(recovered) = recover_fragmented_partial_specialization(
6762                            node,
6763                            child,
6764                            self.source,
6765                            ancestry,
6766                        ) {
6767                            let code_unit = self.visit_named_class_like_shape(
6768                                recovered.declaration_node,
6769                                recovered.name,
6770                                None,
6771                                true,
6772                                Some(recovered.range),
6773                                None,
6774                                None,
6775                                &template_scope,
6776                                stack,
6777                                ancestry,
6778                            );
6779                            self.parsed.record_materialization(
6780                                MaterializationRecord::RecoveredDeclaration {
6781                                    recovery: recovered.range,
6782                                    unit: code_unit.clone(),
6783                                },
6784                            );
6785                            let mut member_scope = template_scope.clone();
6786                            member_scope.class_unit = Some(code_unit);
6787                            member_scope.declarations_are_fields = true;
6788                            member_scope.recovered_specialization_member_scope = true;
6789                            for prefix_member in recovered.prefix_members.into_iter().rev() {
6790                                stack.push(CppWork::Node(CppNodeWork {
6791                                    node: prefix_member,
6792                                    scope: member_scope.clone(),
6793                                }));
6794                            }
6795                            for sibling in recovered.member_siblings {
6796                                self.recovered_class_sibling_scopes
6797                                    .insert(sibling.id(), member_scope.clone());
6798                            }
6799                            for following in recovered.following_declarations.into_iter().rev() {
6800                                stack.push(CppWork::Node(CppNodeWork {
6801                                    node: following,
6802                                    scope: scope.clone(),
6803                                }));
6804                            }
6805                            return;
6806                        }
6807                        stack.push(CppWork::Node(CppNodeWork {
6808                            node: child,
6809                            scope: template_scope,
6810                        }));
6811                    }
6812                }
6813            }
6814            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6815            "linkage_specification" => {
6816                if let Some(body) = cpp_body_node(node) {
6817                    // A linkage specification either wraps a direct declaration or a
6818                    // braced declaration_list. Visit the direct item itself so
6819                    // declaration recoveries still run on its original syntax node.
6820                    let work = if body.kind() == "declaration_list" {
6821                        CppWork::Container(CppContainer {
6822                            node: body,
6823                            scope: scope.clone(),
6824                        })
6825                    } else {
6826                        CppWork::Node(CppNodeWork {
6827                            node: body,
6828                            scope: scope.clone(),
6829                        })
6830                    };
6831                    stack.push(work);
6832                } else {
6833                    stack.push(CppWork::Container(CppContainer {
6834                        node,
6835                        scope: scope.clone(),
6836                    }));
6837                }
6838            }
6839            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6840                self.visit_class_like(node, scope, stack, ancestry)
6841            }
6842            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6843            // A bare namespace-begin sentinel can make tree-sitter promote the
6844            // wrapped declaration to an ERROR node instead of the usual bogus
6845            // function_definition envelope. Keep the recovery entry point on
6846            // the same structured path for both shapes; ordinary ERROR nodes
6847            // retain their declaration-preserving wrapper traversal when the
6848            // sentinel predicate does not match.
6849            "ERROR" => {
6850                // A standalone field-list invocation has no declaration wrapper.
6851                // Claim only an envelope consisting entirely of known invocations.
6852                if node.named_child_count() > 0
6853                    && node.children(&mut node.walk()).all(|child| {
6854                        child.kind() == "identifier"
6855                            && self
6856                                .object_macro_fields
6857                                .contains_key(node_text(child, self.source))
6858                    })
6859                    && self.visit_bare_object_macro_fields(node, scope)
6860                {
6861                    return;
6862                }
6863                self.visit_object_macro_error_classes(node, scope);
6864                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6865                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6866                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6867                        return;
6868                    }
6869                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6870                        return;
6871                    }
6872                    self.visit_macro_swallowed_function_declarations(node, scope);
6873                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
6874                    self.visit_stranded_class_members(node, scope, ancestry);
6875                    stack.push(CppWork::Container(CppContainer {
6876                        node,
6877                        scope: scope.clone(),
6878                    }));
6879                }
6880            }
6881            "declaration" => {
6882                if node.has_error() {
6883                    // Object-like macro lines before a function-like
6884                    // export-macro class head keep the head in a `declaration`
6885                    // rather than in an `ERROR` (#2924). It is the same head
6886                    // and the same following-sibling body, so it takes the same
6887                    // recovery; without it the head's macro invocation indexes
6888                    // as a field named after the macro.
6889                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6890                        return;
6891                    }
6892                }
6893                // The macro-declaration grammar reads a K&R prototype macro
6894                // line as a clean declaration with the name and the macro
6895                // displaced into `macro_modifier` slots, so the prototype
6896                // candidate gate runs on clean declarations too; the candidates
6897                // themselves remain the admission proof.
6898                self.visit_prototype_macro_declarations(node, scope, ancestry);
6899                if self.node_is_inside_consumed_fragment(node) {
6900                    // The whole declaration was a recovered K&R
6901                    // prototype macro invocation (issue #2932); the
6902                    // reparse above already indexed its Function.
6903                    // Falling through to the ordinary declaration
6904                    // visitor would additionally mint the swapped-field
6905                    // wreckage this recovery exists to replace.
6906                    return;
6907                }
6908                if scope.class_unit.is_some()
6909                    && scope.declarations_are_fields
6910                    && scope.recovered_specialization_member_scope
6911                    && let Some(alias_name) =
6912                        recovered_using_declaration_alias_name(node, self.source)
6913                {
6914                    self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6915                } else {
6916                    self.visit_declaration(
6917                        node,
6918                        scope,
6919                        scope.declarations_are_fields,
6920                        stack,
6921                        ancestry,
6922                    )
6923                }
6924            }
6925            // A K&R prototype macro invocation with a pointer return type and a
6926            // simple argument list parses with no ERROR node at all: tree-sitter
6927            // reads `T *name _(( args ));` as a multiplication of a type by a
6928            // call (`T * name_(...)`), wrapped in an `expression_statement`
6929            // that `has_error()` only because of a `MISSING "::"` inside it
6930            // (issue #2932). Nothing else mints a declaration from an
6931            // `expression_statement` at declaration scope today, so there is
6932            // no existing behavior to preserve here if this node is not the
6933            // K&R shape; the admission gate inside
6934            // `visit_prototype_macro_declarations` (exactly one clean function
6935            // prototype spanning the recovered run) is what keeps this arm
6936            // safe on an ordinary errorful expression statement that happens
6937            // to contain nested parentheses.
6938            "expression_statement" => {
6939                if node.has_error() {
6940                    self.visit_prototype_macro_declarations(node, scope, ancestry);
6941                }
6942            }
6943            "field_declaration" => {
6944                // A class-body K&R prototype macro line is the same
6945                // `macro_modifier` shape as a file-scope one, so it takes the
6946                // same candidate gate and reparse (#6902). Only this node's own
6947                // recovery ends its visit: a recovery walk can reach members of
6948                // a region another recovery consumed, and those stay walked.
6949                if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
6950                    self.visit_declaration(node, scope, true, stack, ancestry)
6951                }
6952            }
6953            "preproc_call" => self.visit_preproc_call(node),
6954            "type_definition" | "alias_declaration" => {
6955                self.visit_type_declaration(node, scope, stack, ancestry)
6956            }
6957            "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6958            // `#include` is collected by the coordinated primary events before the
6959            // walk, so a directive the container walk never reaches -- inside
6960            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
6961            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
6962            // include claim.
6963            "preproc_include" => {}
6964            // The grammar represents a split extern-C condition as sibling
6965            // markers; declarations between them remain unconditional file
6966            // scope items and need no special declaration metadata.
6967            "preproc_linkage_open" | "preproc_linkage_close" => {}
6968            kind if preserves_declaration_scope_through_wrapper(
6969                kind,
6970                scope.class_unit.is_some(),
6971            ) =>
6972            {
6973                // A preprocessor conditional gates every declaration inside it
6974                // on a configuration this analyzer never evaluates; record the
6975                // interval so declaration state can say so (issue #1476). The
6976                // else/elif branches are children of the `preproc_if` node, so
6977                // recording the openers covers every branch.
6978                if kind == "labeled_statement" {
6979                    self.visit_access_label_constructor(node, scope, ancestry);
6980                }
6981                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6982                    let mut range = cpp_declaration_range(node);
6983                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6984                        range.end_byte = boundary.end_byte;
6985                        range.end_line = boundary.end_line;
6986                    }
6987                    self.parsed.record_materialization(
6988                        MaterializationRecord::ConfigurationConditional { range },
6989                    );
6990                    if node.has_error() {
6991                        // A malformed export-macro class can close the namespace
6992                        // node early while the enclosing include guard still owns
6993                        // the remaining class-head/body pairs. The ordinary walk
6994                        // cannot carry the lost namespace through those promoted
6995                        // siblings. Scan only structured ERROR nodes in this
6996                        // already-malformed conditional; the pair recovery's
6997                        // exact class/macro/body predicate remains the admission
6998                        // gate, and its namespace lifting restores the owner.
6999                        let mut candidates = vec![node];
7000                        while let Some(candidate) = candidates.pop() {
7001                            // An `ERROR` still inside a namespace body is one
7002                            // the ordinary walk reaches with that namespace in
7003                            // scope. Claiming it here registers the recovered
7004                            // class at file scope and the consumed region then
7005                            // suppresses the walk that would have named it
7006                            // correctly -- which is why Botan's `DL_Group` was
7007                            // `DL_Group` and not `Botan.DL_Group` in the real
7008                            // header, where an include guard wraps the
7009                            // namespace, and was right in a fixture without one
7010                            // (#2552).
7011                            if candidate.kind() == "ERROR"
7012                                && !cpp_is_inside_namespace_body(candidate, ancestry)
7013                                && self.visit_function_like_export_class_pair(
7014                                    candidate, scope, stack, ancestry,
7015                                )
7016                            {
7017                                continue;
7018                            }
7019                            for index in (0..candidate.named_child_count()).rev() {
7020                                candidates.push(
7021                                    candidate
7022                                        .named_child(index)
7023                                        .expect("index below the node's own named child count"),
7024                                );
7025                            }
7026                        }
7027                    }
7028                }
7029                stack.push(CppWork::Container(CppContainer {
7030                    node,
7031                    scope: scope.clone(),
7032                }))
7033            }
7034            // A macro invocation the parser could not group leaves its head in
7035            // whatever slot was free. The `function_definition` and `ERROR`
7036            // arms above try the same recovery on the envelopes they get; a
7037            // flattened run leaves a bare `identifier`/`type_identifier`, and a
7038            // swallowed tail can land in a `parameter_declaration` (#3094).
7039            _ => {
7040                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
7041            }
7042        }
7043    }
7044
7045    fn visit_macro_swallowed_function_declarations<'tree>(
7046        &mut self,
7047        envelope: Node<'tree>,
7048        scope: &ScopeInfo,
7049    ) {
7050        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7051            || envelope.kind() == "ERROR"
7052                && envelope
7053                    .parent()
7054                    .is_some_and(|parent| parent.kind() == "ERROR")
7055        {
7056            return;
7057        }
7058        let mut stack = (0..envelope.named_child_count())
7059            .filter_map(|index| envelope.named_child(index))
7060            .collect::<Vec<_>>();
7061        while let Some(node) = stack.pop() {
7062            if node.kind() == "function_declarator" {
7063                self.visit_error_swallowed_function_declaration(node, scope);
7064            }
7065            for child in named_children_iter(node) {
7066                stack.push(child);
7067            }
7068        }
7069    }
7070
7071    /// Index the declarations an attribute-like macro invocation swallowed into
7072    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
7073    /// the shape and why the parser produces it.
7074    fn visit_macro_wrapped_declarations<'tree>(
7075        &mut self,
7076        envelope: Node<'tree>,
7077        scope: &ScopeInfo,
7078        ancestry: &ParentIndex<'tree>,
7079    ) {
7080        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7081        if recovered.is_empty() {
7082            return;
7083        }
7084        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7085        self.record_recovered_declarations(recovery, envelope, |visitor| {
7086            for declaration in recovered {
7087                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7088            }
7089        });
7090    }
7091
7092    /// Index the declarations a macro invocation collapsed. See
7093    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
7094    /// produces it. Returns whether it claimed the region beginning at
7095    /// `envelope`.
7096    ///
7097    /// The parser's own nodes cannot be read the way the swallowed tail of the
7098    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
7099    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
7100    /// with no declarator left at all, and the declarators that do survive on
7101    /// the envelope's declarator spine pair one declaration's name with the
7102    /// *next* declaration's parameter list. Reading those would be a guess.
7103    ///
7104    /// The bytes are still ordinary declarations, though, and the collapse is
7105    /// the parser carrying the failure forward from one macro invocation. So
7106    /// reparse the region, walk the items the parser makes of it, and when one
7107    /// item is itself a collapsed run, recover that invocation from its own
7108    /// bytes and resume the scan just past it. Each pass starts later than the
7109    /// last, so the scan is a loop over the invocations that collapse, not over
7110    /// the declarations: `llama.h` needs three passes for 214 declarations.
7111    ///
7112    /// The region can reach past `envelope` into the siblings the parser handed
7113    /// the rest of one invocation, so it is recorded as consumed: those
7114    /// siblings are this recovery's, and the ordinary walk must not read them
7115    /// again. Recording it after the scan, not before, keeps the scan's own
7116    /// reparsed nodes -- which carry their original byte offsets -- visible to
7117    /// the walk it drives.
7118    fn visit_collapsed_macro_declaration_run<'tree>(
7119        &mut self,
7120        envelope: Node<'tree>,
7121        scope: &ScopeInfo,
7122        ancestry: &ParentIndex<'tree>,
7123    ) -> bool {
7124        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7125            return false;
7126        };
7127        let start = envelope.start_byte();
7128        let end = run.region_end;
7129        let recovery = cpp_recovery_window(self.source, start, end);
7130        self.record_recovered_declarations(recovery, envelope, |visitor| {
7131            let mut position = start;
7132            while position < end {
7133                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7134                    return;
7135                };
7136                let root = tree.root_node();
7137                // A region reparse is its own tree and needs its own parent
7138                // index and dependency arena; the caller's answers nothing
7139                // about these nodes.
7140                let ancestry = visitor.recovery_ancestry(root);
7141                let mut cursor = root.walk();
7142                let collapsed =
7143                    root.named_children(&mut cursor)
7144                        .enumerate()
7145                        .find_map(|(index, item)| {
7146                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7147                                .map(|run| (index, item, run))
7148                        });
7149                // Walk only the items before the collapsed one. It swallowed
7150                // the rest of the region, so handing it to the ordinary walk
7151                // would re-enter this recovery on a region that starts where
7152                // this one did.
7153                let mut stack = Vec::new();
7154                push_cpp_sibling_range(
7155                    root,
7156                    0,
7157                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7158                    scope.clone(),
7159                    &mut stack,
7160                );
7161                visitor.drain_cpp_work(stack, &ancestry);
7162                let Some((_, item, run)) = collapsed else {
7163                    return;
7164                };
7165                // On its own bytes the invocation is the declaration-scope
7166                // shape `macro_wrapped_declarations` reads, so its wrapped
7167                // declaration needs no reader of its own here.
7168                if let Some(head) =
7169                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7170                {
7171                    let head_root = head.root_node();
7172                    let head_ancestry = visitor.recovery_ancestry(head_root);
7173                    visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7174                }
7175                assert!(
7176                    run.invocation_end > position,
7177                    "a collapsed run at {position} must end after the byte the scan resumed \
7178                     from, but ended at {}",
7179                    run.invocation_end
7180                );
7181                position = run.invocation_end;
7182            }
7183        });
7184        self.consumed_fragment_regions.push((start, end));
7185        true
7186    }
7187
7188    /// Index the members written under an access label that a class-scope
7189    /// macro invocation swallowed. See [`cpp_macro_swallowed_access_label`]
7190    /// for the shape.
7191    ///
7192    /// The members are whole in the source but not in this tree, and at file
7193    /// scope a bodiless member such as `Ctor(int a);` reads as a variable
7194    /// declaration, so the reparse keeps the class around them: the class head
7195    /// through its `{`, the label and its members, and the class's `}`, as one
7196    /// included-range parse at the original offsets. That is the parse the
7197    /// class gets with the invocation's bytes left out, which is what the
7198    /// replacement's own declarations would have to be read without anyway.
7199    /// The reparsed label is a real `access_specifier`, so the members carry the
7200    /// access they are written under. Only the members the label swallowed are
7201    /// walked; the class's other members are the ordinary walk's. When the
7202    /// reparse does not read the label back, nothing is published: the
7203    /// field this node would otherwise mint is named after the access keyword.
7204    fn visit_macro_swallowed_access_label_members<'tree>(
7205        &mut self,
7206        node: Node<'tree>,
7207        label: &CppSwallowedAccessLabel<'tree>,
7208        scope: &ScopeInfo,
7209        ancestry: &ParentIndex<'tree>,
7210    ) {
7211        let Some(body) = ancestry.parent(node) else {
7212            return;
7213        };
7214        let Some(class_like) = ancestry.parent(body) else {
7215            return;
7216        };
7217        // The reparse holds the class itself, so the recovery's source context
7218        // is the class's enclosing one; the class's own scope would name it
7219        // twice in the recovered members' lexical paths.
7220        let Some(class_parent) = ancestry.parent(class_like) else {
7221            return;
7222        };
7223        let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7224            return;
7225        };
7226        if body.kind() != "field_declaration_list"
7227            || open.kind() != "{"
7228            || close.kind() != "}"
7229            || close.is_missing()
7230        {
7231            return;
7232        }
7233        let start = label.keyword.start_byte();
7234        let end = node.end_byte();
7235        let Some(tree) = parse_source_ranges_with_cancellation(
7236            &tree_sitter_cpp::LANGUAGE.into(),
7237            self.source,
7238            &[
7239                (class_like.start_byte(), open.end_byte()),
7240                (start, end),
7241                (close.start_byte(), close.end_byte()),
7242            ],
7243            None,
7244        ) else {
7245            return;
7246        };
7247        let root = tree.root_node();
7248        let Some(reparsed_body) = root
7249            .descendant_for_byte_range(open.start_byte(), open.end_byte())
7250            .and_then(|brace| brace.parent())
7251            .filter(|parent| parent.kind() == "field_declaration_list")
7252        else {
7253            return;
7254        };
7255        let mut cursor = reparsed_body.walk();
7256        let members = reparsed_body
7257            .named_children(&mut cursor)
7258            .enumerate()
7259            .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7260            .map(|(index, member)| (index, member.kind(), member.start_byte()))
7261            .collect::<Vec<_>>();
7262        let Some(&(first, "access_specifier", label_start)) = members.first() else {
7263            return;
7264        };
7265        if label_start != start {
7266            return;
7267        }
7268        let last = members.last().expect("the label is a member").0;
7269        let reparsed_ancestry = self.recovery_ancestry(root);
7270        let recovery = cpp_recovery_window(self.source, start, end);
7271        self.record_recovered_declarations(recovery, class_parent, |visitor| {
7272            let mut stack = Vec::new();
7273            push_cpp_sibling_range(
7274                reparsed_body,
7275                first + 1,
7276                last + 1,
7277                scope.clone(),
7278                &mut stack,
7279            );
7280            visitor.drain_cpp_work(stack, &reparsed_ancestry);
7281        });
7282    }
7283
7284    /// Index the members a string-argument attribute macro stranded in an
7285    /// `ERROR` inside a class body.
7286    ///
7287    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
7288    /// grouping of the attributed member and of every member written after it
7289    /// until it recovers. The members are still whole `function_declarator`
7290    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
7291    /// export-macro class such as Botan's `DL_Group` was indexed with no
7292    /// members at all (#2552).
7293    fn visit_stranded_class_members<'tree>(
7294        &mut self,
7295        node: Node<'tree>,
7296        scope: &ScopeInfo,
7297        ancestry: &ParentIndex<'tree>,
7298    ) {
7299        if scope.class_unit.is_none() || !scope.declarations_are_fields {
7300            return;
7301        }
7302        for member in stranded_declaration_run(node, self.source).declarations {
7303            self.add_macro_wrapped_declaration(member, scope, ancestry);
7304        }
7305    }
7306
7307    /// Index the constructor an access label swallowed in a reparsed class
7308    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
7309    ///
7310    /// The declarator is recovered by reparsing from that call to the end of
7311    /// the label's own statement, which is the same offset-preserving region
7312    /// reparse every other recovery here uses, so the recovered nodes carry
7313    /// their true source positions.
7314    fn visit_access_label_constructor<'tree>(
7315        &mut self,
7316        node: Node<'tree>,
7317        scope: &ScopeInfo,
7318        ancestry: &ParentIndex<'tree>,
7319    ) {
7320        let Some(class_unit) = scope.class_unit.clone() else {
7321            return;
7322        };
7323        if !scope.declarations_are_fields {
7324            return;
7325        }
7326        let class_name = class_unit.identifier().to_string();
7327        // Statement context reads pointer parameters as multiplication. Use the
7328        // same class-context validation as an unlabelled constructor statement.
7329        let statement = node.named_children(&mut node.walk()).find(|child| {
7330            cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7331        });
7332        if let Some(statement) = statement {
7333            self.visit_bare_constructor_expression(statement, scope, ancestry);
7334            return;
7335        }
7336        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7337        else {
7338            return;
7339        };
7340        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7341            return;
7342        };
7343        let root = tree.root_node();
7344        let Some(declarator) =
7345            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7346        else {
7347            return;
7348        };
7349        let reparsed_ancestry = self.recovery_ancestry(root);
7350        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7351        let range = cpp_declaration_range(definition.unwrap_or(declarator));
7352        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7353        self.record_recovered_declarations(recovery, node, |visitor| {
7354            visitor.add_macro_wrapped_declaration(
7355                MacroWrappedDeclaration {
7356                    declarator,
7357                    range,
7358                    is_static: false,
7359                },
7360                scope,
7361                &reparsed_ancestry,
7362            );
7363        });
7364        // This constructor has been accounted for. Its statement-context
7365        // initializer declarators do not declare additional members.
7366        self.consumed_fragment_regions.push((start, range.end_byte));
7367    }
7368
7369    /// Index the members a string-argument attribute macro stranded inside the
7370    /// `macro_type_specifier` the macro-declaration grammar reads them as (#2552
7371    /// shape 3 under the new grammar).
7372    ///
7373    /// `BOTAN_DEPRECATED("text") explicit Ctor(T); Ctor(U, V);` keeps the macro as
7374    /// a `macro_type_specifier` whose argument region is an `ERROR` holding the
7375    /// string, the attributed member as a `type_descriptor`, and its `;`, while
7376    /// the member after it becomes the specifier's own `type` field. Neither
7377    /// member has a real declarator node -- an abstract function declarator in a
7378    /// `type_descriptor` carries only parameters -- but each member's source range
7379    /// is whole, so reparse it and read the declarator from the reparse, which is
7380    /// the same offset-preserving region reparse every other recovery here uses.
7381    /// The admission gate is the same one every reparse shares: exactly one clean
7382    /// callable declaration spanning the member's range.
7383    fn visit_string_attribute_macro_type_members<'tree>(
7384        &mut self,
7385        node: Node<'tree>,
7386        scope: &ScopeInfo,
7387    ) -> bool {
7388        if node.kind() != "field_declaration" {
7389            return false;
7390        }
7391        let Some(specifier) = node
7392            .child_by_field_name("type")
7393            .filter(|type_node| type_node.kind() == "macro_type_specifier")
7394        else {
7395            return false;
7396        };
7397        if specifier
7398            .child_by_field_name("name")
7399            .is_none_or(|name| name.kind() != "identifier")
7400        {
7401            return false;
7402        }
7403        // The attributed member sits inside the argument `ERROR`: the string's
7404        // quote is the first child, the member's `;` the last, and its
7405        // type-descriptor is in between.
7406        let mut cursor = specifier.walk();
7407        let Some(argument_error) = specifier
7408            .named_children(&mut cursor)
7409            .find(|child| child.kind() == "ERROR")
7410        else {
7411            return false;
7412        };
7413        if argument_error
7414            .child(0)
7415            .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7416            || argument_error
7417                .child(argument_error.child_count().saturating_sub(1))
7418                .is_none_or(|last| last.kind() != ";" || last.is_missing())
7419        {
7420            return false;
7421        }
7422        let mut members: Vec<((usize, usize), String)> = Vec::new();
7423        for index in 0..argument_error.child_count() {
7424            let Some(child) = argument_error.child(index) else {
7425                continue;
7426            };
7427            if child.kind() == "type_descriptor" {
7428                let start = (0..index)
7429                    .rev()
7430                    .find_map(|previous| {
7431                        argument_error
7432                            .child(previous)
7433                            .filter(|sibling| sibling.kind() == "explicit")
7434                    })
7435                    .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7436                let Some(name) = child
7437                    .child_by_field_name("type")
7438                    .and_then(|type_node| direct_identifier_name(type_node, self.source))
7439                else {
7440                    return false;
7441                };
7442                members.push(((start, argument_error.end_byte()), name));
7443            }
7444        }
7445        let [(first_range, first_name)] = members.as_slice() else {
7446            return false;
7447        };
7448        let Some(second) = specifier
7449            .child_by_field_name("type")
7450            .filter(|type_node| type_node.kind() == "type_descriptor")
7451        else {
7452            return false;
7453        };
7454        let Some(second_name) = second
7455            .child_by_field_name("type")
7456            .and_then(|type_node| direct_identifier_name(type_node, self.source))
7457        else {
7458            return false;
7459        };
7460        let members = [
7461            (*first_range, first_name.as_str()),
7462            ((second.start_byte(), node.end_byte()), second_name.as_str()),
7463        ];
7464        let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7465        self.record_recovered_declarations(recovery, node, |visitor| {
7466            for ((start, end), member_name) in members {
7467                let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7468                    continue;
7469                };
7470                let root = tree.root_node();
7471                // The file-scope reparse of a member-shaped head is errorful
7472                // (a member qualifier is not valid there), so the admission is
7473                // the declarator itself: exactly one function declarator
7474                // naming the member inside its own range.
7475                let mut candidate = None;
7476                let mut stack = vec![root];
7477                while let Some(current) = stack.pop() {
7478                    if current.kind() == "function_declarator"
7479                        && current.start_byte() >= start
7480                        && current.end_byte() <= end
7481                        && cpp_function_declarator_name_node(current)
7482                            .is_some_and(|name| node_text(name, visitor.source) == member_name)
7483                    {
7484                        if candidate.is_some() {
7485                            candidate = None;
7486                            break;
7487                        }
7488                        candidate = Some(current);
7489                        continue;
7490                    }
7491                    let mut cursor = current.walk();
7492                    stack.extend(current.named_children(&mut cursor));
7493                }
7494                let Some(declarator) = candidate else {
7495                    continue;
7496                };
7497                let reparsed_ancestry = visitor.recovery_ancestry(root);
7498                visitor.add_macro_wrapped_declaration(
7499                    MacroWrappedDeclaration {
7500                        declarator,
7501                        range: cpp_recovery_window(visitor.source, start, end),
7502                        is_static: false,
7503                    },
7504                    scope,
7505                    &reparsed_ancestry,
7506                );
7507            }
7508        });
7509        true
7510    }
7511
7512    fn add_macro_wrapped_declaration<'tree>(
7513        &mut self,
7514        declaration: MacroWrappedDeclaration<'tree>,
7515        scope: &ScopeInfo,
7516        ancestry: &ParentIndex<'tree>,
7517    ) {
7518        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7519        else {
7520            return;
7521        };
7522        let code_unit =
7523            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7524        let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7525        let is_constructor = scope
7526            .class_unit
7527            .as_ref()
7528            .is_some_and(|class| function.name == class.identifier())
7529            || cpp_callable_is_structural_constructor(
7530                declaration.declarator,
7531                self.source,
7532                ancestry,
7533            );
7534        let already_declared = self.parsed.contains_declaration(&code_unit);
7535        if already_declared {
7536            self.parsed
7537                .record_navigation_range(code_unit.clone(), declaration.range);
7538            if definition.is_none() {
7539                return;
7540            }
7541            // Preserve the earlier declaration-only occurrence while adding
7542            // the recovered body as a distinct definition occurrence.
7543        }
7544        self.add_declaration_with_range(
7545            ancestry,
7546            code_unit.clone(),
7547            declaration.range,
7548            Some(declaration.declarator),
7549            None,
7550            None,
7551        );
7552        if definition.is_some() {
7553            let recovered_declaration = *self
7554                .source_links
7555                .get(&code_unit)
7556                .and_then(|links| links.last())
7557                .expect("recovered definition source");
7558            let mut properties = self.source_properties.borrow_mut();
7559            let fact = properties
7560                .get_mut(&recovered_declaration)
7561                .expect("recovered definition source fact");
7562            fact.occurrence_role =
7563                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7564            fact.callable_is_constructor |= is_constructor;
7565        }
7566        let signature = normalize_cpp_whitespace(
7567            self.source
7568                .get(declaration.range.start_byte..declaration.range.end_byte)
7569                .expect("a recovered declaration range covers one source range"),
7570        );
7571        let linkage = if declaration.is_static {
7572            CallableLinkage::Internal
7573        } else {
7574            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7575        };
7576        // Whether this is a definition is a property of the recovered node, not
7577        // of the caller: a declarator that a `function_definition` gives a body
7578        // is a definition wherever the recovery found it.
7579        let declaration_only = definition.is_none();
7580        self.add_signature_with_metadata(
7581            code_unit.clone(),
7582            cpp_signature_metadata(
7583                signature,
7584                declaration.declarator,
7585                linkage,
7586                self.source,
7587                ancestry,
7588            )
7589            .with_declaration_only(declaration_only),
7590        );
7591        if already_declared {
7592            return;
7593        }
7594        if let Some(parent) = &scope.class_unit {
7595            self.parsed.add_child(parent.clone(), code_unit);
7596        } else if let Some(module) = &scope.module {
7597            self.parsed.add_child(module.clone(), code_unit);
7598        }
7599    }
7600
7601    /// Index the local types of an ordinary function body. Function bodies are
7602    /// otherwise outside the declaration walk, but their anonymous aggregate
7603    /// owners must exist so structured local binding inference can name their
7604    /// fields (#2994), and a named C++ local class and its members are query
7605    /// targets of their own (#6902).
7606    fn visit_local_types_in_function<'tree>(
7607        &mut self,
7608        function: Node<'tree>,
7609        callable: &CodeUnit,
7610        scope: &ScopeInfo,
7611        stack: &mut Vec<CppWork<'tree>>,
7612        ancestry: &ParentIndex<'tree>,
7613    ) {
7614        let Some(body) = cpp_body_node(function) else {
7615            return;
7616        };
7617        // A body opens a block scope, so nothing declared in it is a member of
7618        // the class the definition may be written inside. Dropping the
7619        // aggregate carrier gives an inline member function's body the same
7620        // block scope an out-of-line `void C::f()` body already has; without
7621        // it a C++ header, where member bodies are usually inline, would keep
7622        // none of these owners (#3301).
7623        let block_scope;
7624        let scope = if scope.class_unit.is_some() {
7625            block_scope = ScopeInfo {
7626                class_unit: None,
7627                declarations_are_fields: false,
7628                ..scope.clone()
7629            };
7630            &block_scope
7631        } else {
7632            scope
7633        };
7634        let mut pending = vec![body];
7635        while let Some(node) = pending.pop() {
7636            if matches!(node.kind(), "function_definition" | "lambda_expression") {
7637                continue;
7638            }
7639            if node.kind() == "declaration"
7640                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7641            {
7642                continue;
7643            }
7644            if matches!(
7645                node.kind(),
7646                "class_specifier" | "struct_specifier" | "union_specifier"
7647            ) {
7648                self.visit_function_local_class(node, callable, scope, stack, ancestry);
7649                continue;
7650            }
7651            let mut cursor = node.walk();
7652            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7653            children.reverse();
7654            pending.extend(children);
7655        }
7656    }
7657
7658    /// Mint a named C++ class defined in a function body under the identity
7659    /// of the block it is written in (see [`CppLocalBlock`]). Its members are
7660    /// owned by the class through the ordinary class-body walk.
7661    ///
7662    /// C block-scope tags are a separate capability and keep their current
7663    /// absence. A header is read both ways, so a minted local class is a
7664    /// declaration whose existence differs between the two readings, which is
7665    /// what the C-reading witness records.
7666    fn visit_function_local_class<'tree>(
7667        &mut self,
7668        node: Node<'tree>,
7669        callable: &CodeUnit,
7670        scope: &ScopeInfo,
7671        stack: &mut Vec<CppWork<'tree>>,
7672        ancestry: &ParentIndex<'tree>,
7673    ) {
7674        if self.c_tag_semantics || cpp_body_node(node).is_none() {
7675            return;
7676        }
7677        let Some(name) = class_like_name(node, self.source, ancestry) else {
7678            return;
7679        };
7680        let block =
7681            std::iter::successors(ancestry.parent(node), |current| ancestry.parent(*current))
7682                .find(|ancestor| ancestor.kind() == "compound_statement")
7683                .expect("a function-local class is written inside the function body");
7684        self.c_tag_scope_witness = true;
7685        let local_scope = ScopeInfo {
7686            package_name: callable.package_name().to_string(),
7687            class_unit: None,
7688            template_signature: None,
7689            template_metadata: None,
7690            declarations_are_fields: false,
7691            recovered_specialization_member_scope: false,
7692            local_block: Some(CppLocalBlock::new(callable, block)),
7693            ..scope.clone()
7694        };
7695        self.visit_named_class_like(node, name, &local_scope, stack, ancestry);
7696    }
7697
7698    fn visit_error_swallowed_function_declaration<'tree>(
7699        &mut self,
7700        node: Node<'tree>,
7701        scope: &ScopeInfo,
7702    ) {
7703        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7704            return;
7705        };
7706        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7707            return;
7708        };
7709        let root = tree.root_node();
7710        let mut cursor = root.walk();
7711        let declarations = root
7712            .named_children(&mut cursor)
7713            .filter(|child| child.kind() != "comment")
7714            .collect::<Vec<_>>();
7715        let [declaration] = declarations.as_slice() else {
7716            return;
7717        };
7718        if declaration.kind() != "declaration"
7719            || declaration.has_error()
7720            || declaration.start_byte() != start
7721            || declaration.end_byte() != end
7722        {
7723            return;
7724        }
7725        let recovery = cpp_recovery_window(self.source, start, end);
7726        // The reparsed region is its own tree, so this walk indexes it itself.
7727        let reparsed_ancestry = self.recovery_ancestry(root);
7728        self.record_recovered_declarations(recovery, node, |visitor| {
7729            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7730        });
7731    }
7732
7733    /// Recover a K&R-compatibility prototype macro invocation such as
7734    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
7735    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
7736    /// defined only in headers outside this workspace (ruby's own
7737    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
7738    /// `name _((args))` and turns each such line into a malformed
7739    /// `declaration` or pointer-expression statement. Before this
7740    /// recovery, the ordinary declaration visitor read that wreckage as a
7741    /// field with the type and name swapped (e.g. a field literally named
7742    /// `VALUE`) and indexed no prototype for the real name at all.
7743    ///
7744    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
7745    /// preserve the declared name, a known one-argument compatibility macro,
7746    /// the nested argument list, and a live terminator. It rejects arbitrary
7747    /// identifiers and bare `ERROR` envelopes that flattened those relations;
7748    /// guessing there previously turned malformed non-macro declarations into
7749    /// invented functions. For each proven candidate this reparses the range
7750    /// before the macro token, the inner parenthesized argument node, and the
7751    /// terminating `;` as one included-range parse. That is the parser-native
7752    /// equivalent of expanding `#define _(args) args` while retaining original
7753    /// byte offsets. The result is admitted only when it is exactly one clean
7754    /// function prototype spanning the recovered run.
7755    ///
7756    /// A class-body line is a `field_declaration`, and a bodiless member read
7757    /// at file scope loses the class-body position its `static` and access
7758    /// facts depend on. That reparse therefore keeps the class around the
7759    /// prototype, as [`PrototypeMacroClassEnvelope`] describes, and admits
7760    /// exactly one clean member prototype in the reparsed body instead.
7761    ///
7762    /// Returns whether a candidate was recovered.
7763    fn visit_prototype_macro_declarations<'tree>(
7764        &mut self,
7765        node: Node<'tree>,
7766        scope: &ScopeInfo,
7767        ancestry: &ParentIndex<'tree>,
7768    ) -> bool {
7769        let candidates = cpp_prototype_macro_candidates(node, self.source);
7770        if candidates.is_empty() {
7771            return false;
7772        }
7773        let envelope = if node.kind() == "field_declaration" {
7774            let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7775            else {
7776                return false;
7777            };
7778            Some(envelope)
7779        } else {
7780            None
7781        };
7782        let mut recovered = false;
7783        for candidate in candidates {
7784            let start = candidate.run_start;
7785            let end = candidate.semicolon_end;
7786            if self.byte_range_is_inside_consumed_fragment(start, end) {
7787                continue;
7788            }
7789            let mut ranges = Vec::with_capacity(6);
7790            if let Some(envelope) = &envelope {
7791                ranges.push(envelope.head);
7792                ranges.extend(envelope.label);
7793            }
7794            ranges.extend(candidate.ranges());
7795            if let Some(envelope) = &envelope {
7796                ranges.push(envelope.close);
7797            }
7798            let Some(tree) = parse_source_ranges_with_cancellation(
7799                &tree_sitter_cpp::LANGUAGE.into(),
7800                self.source,
7801                &ranges,
7802                None,
7803            ) else {
7804                continue;
7805            };
7806            let root = tree.root_node();
7807            let (container, expected_kind) = match &envelope {
7808                None => (root, "declaration"),
7809                Some(envelope) => {
7810                    let Some(body) = root
7811                        .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7812                        .and_then(|brace| brace.parent())
7813                        .filter(|parent| parent.kind() == "field_declaration_list")
7814                    else {
7815                        continue;
7816                    };
7817                    (body, "field_declaration")
7818                }
7819            };
7820            // Everything the envelope contributes ends before the prototype.
7821            let mut cursor = container.walk();
7822            let declarations = container
7823                .named_children(&mut cursor)
7824                .enumerate()
7825                .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7826                .collect::<Vec<_>>();
7827            let [(index, declaration)] = declarations.as_slice() else {
7828                continue;
7829            };
7830            if declaration.kind() != expected_kind
7831                || declaration.has_error()
7832                || declaration.start_byte() != start
7833                || declaration.end_byte() != end
7834                || declaration
7835                    .child_by_field_name("declarator")
7836                    .and_then(extract_function_declarator)
7837                    .is_none()
7838            {
7839                continue;
7840            }
7841            let index = *index;
7842            let recovery = cpp_recovery_window(self.source, start, end);
7843            // The reparsed region is its own tree, so this walk indexes it
7844            // itself. A class envelope holds the class, so the recovery's
7845            // source context is the class's enclosing one; the class's own
7846            // would name it twice in the member's lexical path.
7847            let reparsed_ancestry = self.recovery_ancestry(root);
7848            let origin = envelope
7849                .as_ref()
7850                .map_or(node, |envelope| envelope.class_parent);
7851            self.record_recovered_declarations(recovery, origin, |visitor| {
7852                let mut stack = Vec::new();
7853                push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7854                visitor.drain_cpp_work(stack, &reparsed_ancestry);
7855            });
7856            self.consumed_fragment_regions.push((start, end));
7857            recovered = true;
7858        }
7859        recovered
7860    }
7861
7862    /// Declare one Module per namespace level of `levels` under `package_name`,
7863    /// each recording the range of the construct that writes it, and return the
7864    /// innermost level's package name and Module. A level an earlier definition
7865    /// already declared is reused, which is also how a reopened namespace keeps
7866    /// the range of the definition that first declared it.
7867    fn declare_namespace_levels(
7868        &mut self,
7869        mut package_name: String,
7870        levels: Vec<(String, Range, Option<Range>)>,
7871        node: Node<'_>,
7872        ancestry: &ParentIndex<'_>,
7873    ) -> (String, Option<CodeUnit>) {
7874        let mut module = None;
7875        for (component, range, name_range) in levels {
7876            let full_name = if package_name.is_empty() {
7877                component
7878            } else {
7879                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7880            };
7881            let level = CodeUnit::new_fq(
7882                self.file.clone(),
7883                CodeUnitType::Module,
7884                "",
7885                full_name.clone(),
7886                cpp_namespace_fq(&full_name),
7887            );
7888            if !self.parsed.contains_declaration(&level) {
7889                if let Some(name_range) = name_range {
7890                    self.add_declaration_with_name_range(
7891                        level.clone(),
7892                        range,
7893                        name_range,
7894                        Some((ancestry, node)),
7895                        None,
7896                        None,
7897                    );
7898                } else {
7899                    self.add_declaration_with_range(
7900                        ancestry,
7901                        level.clone(),
7902                        range,
7903                        Some(node),
7904                        None,
7905                        None,
7906                    );
7907                }
7908            }
7909            package_name = full_name;
7910            module = Some(level);
7911        }
7912        (package_name, module)
7913    }
7914
7915    /// [`Self::declare_namespace_levels`] for levels a recovered-namespace
7916    /// index restores. Each level carries the range of its own name token,
7917    /// which the walk's current node cannot supply: that node is whichever
7918    /// declaration the walk reached first inside the region, and deriving a
7919    /// level's name from it paired the level's range with an unrelated
7920    /// construct's name token (#4292).
7921    fn declare_recovered_namespace_levels(
7922        &mut self,
7923        mut package_name: String,
7924        levels: Vec<(String, Range, Range)>,
7925    ) -> (String, Option<CodeUnit>) {
7926        let mut module = None;
7927        for (component, range, name_range) in levels {
7928            let full_name = if package_name.is_empty() {
7929                component
7930            } else {
7931                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7932            };
7933            let level = CodeUnit::new_fq(
7934                self.file.clone(),
7935                CodeUnitType::Module,
7936                "",
7937                full_name.clone(),
7938                cpp_namespace_fq(&full_name),
7939            );
7940            if !self.parsed.contains_declaration(&level) {
7941                self.add_declaration_with_name_range(
7942                    level.clone(),
7943                    range,
7944                    name_range,
7945                    None,
7946                    None,
7947                    None,
7948                );
7949            }
7950            package_name = full_name;
7951            module = Some(level);
7952        }
7953        (package_name, module)
7954    }
7955
7956    /// Restore the complete lexical namespace path when recovery dropped an
7957    /// ancestor or retained it past its real close. Parsed namespaces beginning
7958    /// inside the recovered region still contribute their normal nesting.
7959    /// Crossing that boundary also clears any malformed class owner.
7960    fn recovered_namespace_scope(
7961        &mut self,
7962        node: Node<'_>,
7963        scope: &ScopeInfo,
7964    ) -> Option<ScopeInfo> {
7965        self.orphaned_namespaces.region_at(node.start_byte())?;
7966        // Each level carries the `namespace Name { ... }` construct that writes
7967        // it along with the level's own name token. Declaring them at `node`
7968        // instead recorded the range of whichever member the walk happened to
7969        // reach first, so a namespace whose head collapsed into an ERROR
7970        // reported its first inner declaration's lines as its own (#3309), and
7971        // deriving the name from that node paired the level's range with the
7972        // member's name token (#4292).
7973        let levels = self
7974            .orphaned_namespaces
7975            .enclosing_namespace_levels(node, self.source);
7976        let package_name = levels
7977            .iter()
7978            .map(|(component, ..)| component.as_str())
7979            .collect::<Vec<_>>()
7980            .join(CPP_PACKAGE_SEPARATOR);
7981        if package_name == scope.package_name {
7982            return None;
7983        }
7984        let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7985        Some(ScopeInfo {
7986            package_name,
7987            module,
7988            // A recovered namespace body is no longer inside the class (or
7989            // function) that tree-sitter accidentally used as its wrapper.
7990            // Keep namespace/module context, but never carry that owner into
7991            // declarations published from the recovered region.
7992            class_unit: None,
7993            template_signature: None,
7994            template_metadata: None,
7995            declarations_are_fields: false,
7996            recovered_specialization_member_scope: false,
7997            visible_using_namespaces: scope.visible_using_namespaces.clone(),
7998            local_block: None,
7999            visible_using_declarations: scope.visible_using_declarations.clone(),
8000        })
8001    }
8002
8003    fn visit_namespace<'tree>(
8004        &mut self,
8005        node: Node<'tree>,
8006        scope: &ScopeInfo,
8007        stack: &mut Vec<CppWork<'tree>>,
8008        ancestry: &ParentIndex<'tree>,
8009    ) {
8010        let name_node = node.child_by_field_name("name");
8011        let Some(name_node) = name_node else {
8012            if let Some(body) = cpp_body_node(node) {
8013                stack.push(CppWork::Container(CppContainer {
8014                    node: body,
8015                    scope: scope.clone(),
8016                }));
8017            }
8018            return;
8019        };
8020        // Diagnostic corpora contain deliberately ill-formed global namespace
8021        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
8022        // the leading global `::` as the first anonymous child. Honor that AST
8023        // boundary instead of appending the name to the lexical namespace;
8024        // appending produced legacy names such as `outer::::outer::inner`, which
8025        // could not round-trip through the structured FqName boundary.
8026        let explicitly_global = name_node
8027            .child(0)
8028            .is_some_and(|child| !child.is_named() && child.kind() == "::");
8029        let components = cpp_namespace_name_components(name_node, self.source);
8030        if components.is_empty() {
8031            return;
8032        }
8033        // One Module per namespace level. C++17's `namespace a::b { ... }` is
8034        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
8035        // shorthand must declare `a` as well as `a::b` -- extracting only the
8036        // innermost level left the enclosing namespace undeclared and made the
8037        // two spellings of one construct disagree (issue #1878).
8038        let package_name = if explicitly_global {
8039            String::new()
8040        } else {
8041            scope.package_name.clone()
8042        };
8043        let definition = node_range(node);
8044        // The `namespace a::b` shorthand names every level in one node: each
8045        // level's name occurrence is its own token, the same evidence the
8046        // recovered-namespace path carries per level (#4292).
8047        let mut component_ranges =
8048            cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
8049        let (package_name, module) = self.declare_namespace_levels(
8050            package_name,
8051            components
8052                .into_iter()
8053                .map(|component| {
8054                    let name_range = component_ranges
8055                        .next()
8056                        .filter(|(name, _)| *name == component)
8057                        .map(|(_, range)| range);
8058                    (component, definition, name_range)
8059                })
8060                .collect(),
8061            node,
8062            ancestry,
8063        );
8064
8065        let namespace_scope = ScopeInfo {
8066            package_name,
8067            module,
8068            // C++ never nests a namespace inside a class, so a surviving
8069            // class_unit here is always recovery bleed: a malformed-region
8070            // boundary upstream mis-scoped this namespace block. Keeping the
8071            // owner would mint the namespace's declarations as class members
8072            // under a re-appended package, desyncing the fq boundary assert
8073            // (#2306). Dropping it is identity-neutral for valid code, where
8074            // class_unit is always empty at a namespace definition.
8075            class_unit: None,
8076            template_signature: scope.template_signature.clone(),
8077            template_metadata: scope.template_metadata.clone(),
8078            declarations_are_fields: false,
8079            recovered_specialization_member_scope: false,
8080            visible_using_namespaces: scope.visible_using_namespaces.clone(),
8081            local_block: None,
8082            visible_using_declarations: scope.visible_using_declarations.clone(),
8083        };
8084        let container = cpp_body_node(node).unwrap_or(node);
8085        // A malformed export-macro class body may turn the following class
8086        // into a descendant of a bogus function/labeled/error envelope. Those
8087        // descendants are not declaration containers and the ordinary walk
8088        // intentionally does not descend into them. Scan the namespace tree
8089        // once for the strict embedded class geometry before scheduling its
8090        // normal declarations. When one envelope matches, its helper recovers
8091        // every embedded class and the walk need not inspect its descendants.
8092        let mut candidates = vec![container];
8093        while let Some(candidate) = candidates.pop() {
8094            if matches!(
8095                candidate.kind(),
8096                "ERROR" | "function_definition" | "labeled_statement"
8097            ) && self.visit_embedded_function_like_export_classes(
8098                candidate,
8099                &namespace_scope,
8100                stack,
8101                ancestry,
8102            ) {
8103                continue;
8104            }
8105            for index in (0..candidate.named_child_count()).rev() {
8106                candidates.push(
8107                    candidate
8108                        .named_child(index)
8109                        .expect("index below the node's own named child count"),
8110                );
8111            }
8112        }
8113        stack.push(CppWork::Container(CppContainer {
8114            node: container,
8115            scope: namespace_scope,
8116        }));
8117    }
8118
8119    fn visit_class_like<'tree>(
8120        &mut self,
8121        node: Node<'tree>,
8122        scope: &ScopeInfo,
8123        stack: &mut Vec<CppWork<'tree>>,
8124        ancestry: &ParentIndex<'tree>,
8125    ) {
8126        let Some(name) = class_like_name(node, self.source, ancestry) else {
8127            return;
8128        };
8129        let name = qualified_class_name_chain(node, self.source, scope)
8130            .map(|chain| chain.join("$"))
8131            .unwrap_or(name);
8132        self.visit_named_class_like(node, name, scope, stack, ancestry);
8133    }
8134
8135    fn visit_named_class_like<'tree>(
8136        &mut self,
8137        node: Node<'tree>,
8138        name: String,
8139        scope: &ScopeInfo,
8140        stack: &mut Vec<CppWork<'tree>>,
8141        ancestry: &ParentIndex<'tree>,
8142    ) {
8143        let body = cpp_body_node(node);
8144        let definition_body_present = body.is_some();
8145        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8146            .then(|| extract_cpp_supertypes(node, self.source));
8147        self.visit_named_class_like_shape(
8148            node,
8149            name,
8150            body,
8151            definition_body_present,
8152            None,
8153            raw_supertypes,
8154            Some(extract_cpp_base_facts(node, self.source)),
8155            scope,
8156            stack,
8157            ancestry,
8158        );
8159    }
8160
8161    /// Whether this class-like declaration is a C tag that belongs to the
8162    /// enclosing non-aggregate scope rather than to the aggregate it is
8163    /// lexically written inside.
8164    ///
8165    /// `class_specifier` is deliberately excluded: `class` is not C, so text
8166    /// that spells one in a `.c` file is not C code and keeps the C++ reading
8167    /// rather than getting a half-C identity.
8168    fn mints_tag_at_enclosing_c_scope(
8169        &self,
8170        declaration_node: Node<'_>,
8171        scope: &ScopeInfo,
8172        ancestry: &ParentIndex<'_>,
8173    ) -> bool {
8174        if scope.class_unit.is_none()
8175            || !matches!(
8176                declaration_node.kind(),
8177                "struct_specifier" | "union_specifier" | "enum_specifier"
8178            )
8179            || class_like_name(declaration_node, self.source, ancestry).is_none()
8180        {
8181            return false;
8182        }
8183        // A class-specifier ancestor proves this is C++ syntax. A C reading of
8184        // the header must not apply C tag hoisting to structs nested in that
8185        // class, because that invents a file-scope identity for its members.
8186        let mut ancestor = ancestry.parent(declaration_node);
8187        while let Some(current) = ancestor {
8188            if current.kind() == "class_specifier" {
8189                return false;
8190            }
8191            ancestor = ancestry.parent(current);
8192        }
8193        true
8194    }
8195
8196    #[allow(clippy::too_many_arguments)]
8197    fn visit_named_class_like_shape<'tree>(
8198        &mut self,
8199        declaration_node: Node<'tree>,
8200        name: String,
8201        body: Option<Node<'tree>>,
8202        definition_body_present: bool,
8203        explicit_range: Option<Range>,
8204        raw_supertypes: Option<Vec<String>>,
8205        base_facts: Option<Vec<CppBaseSpecifierFact>>,
8206        scope: &ScopeInfo,
8207        stack: &mut Vec<CppWork<'tree>>,
8208        ancestry: &ParentIndex<'tree>,
8209    ) -> CodeUnit {
8210        let displaced_macro_tail = if explicit_range.is_none() {
8211            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8212        } else {
8213            None
8214        };
8215        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8216        let recovered_scope = self.scope_for_recovered_exported_class(
8217            declaration_node,
8218            &name,
8219            definition_body_present,
8220            scope,
8221            ancestry,
8222        );
8223        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
8224        // aggregate's member list is declared at the enclosing non-aggregate
8225        // scope, not nested inside the aggregate. `scope.class_unit` is the
8226        // only aggregate carrier in this walk, so dropping it puts the tag at
8227        // the nearest enclosing non-aggregate scope -- the module at file or
8228        // namespace scope, and the same block-scope representation a
8229        // function-local aggregate already gets. The tag's own body scope
8230        // below still owns its members, so fields and enumerators are
8231        // unaffected.
8232        let c_tag_scope;
8233        let c_tag_scope_applies =
8234            self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8235        if !self.c_tag_semantics && c_tag_scope_applies {
8236            self.c_tag_scope_witness = true;
8237        }
8238        let scope = if self.c_tag_semantics && c_tag_scope_applies {
8239            c_tag_scope = ScopeInfo {
8240                class_unit: None,
8241                ..recovered_scope.clone()
8242            };
8243            &c_tag_scope
8244        } else {
8245            &recovered_scope
8246        };
8247        let local_block = scope
8248            .local_block
8249            .as_ref()
8250            .filter(|_| scope.class_unit.is_none());
8251        let short_name = if let Some(parent) = &scope.class_unit {
8252            cpp_join_nested_short(parent.short_name(), &name)
8253        } else if let Some(block) = local_block {
8254            cpp_join_nested_short(&block.short_name, &name)
8255        } else {
8256            name.clone()
8257        };
8258        // A top-level out-of-line qualified class definition (`struct
8259        // Outer::Inner { ... }` inside its namespace, #2246) carries its
8260        // nesting chain as the `$`-joined display name; push one Type/Nested
8261        // segment per class so segment-pop owner navigation keeps working.
8262        // Every other leaf name stays opaque so a literal `$` in a source
8263        // identifier never crosses the split/join boundary (#2140).
8264        let qualified_chain = if scope.class_unit.is_none() && local_block.is_none() {
8265            qualified_class_name_chain(declaration_node, self.source, scope)
8266                .filter(|chain| chain.join("$") == name)
8267        } else {
8268            None
8269        };
8270        let fq = if let Some(block) = local_block {
8271            block
8272                .fq
8273                .clone()
8274                .with_pushed(segment_interner().intern(&name, SegmentKind::Nested))
8275        } else if let Some(chain) = qualified_chain {
8276            let mut fq = FqName::new();
8277            cpp_push_package(&mut fq, &scope.package_name);
8278            let mut first = true;
8279            for component in chain {
8280                let kind = if first {
8281                    SegmentKind::Type
8282                } else {
8283                    SegmentKind::Nested
8284                };
8285                fq.push(segment_interner().intern(&component, kind));
8286                first = false;
8287            }
8288            fq
8289        } else {
8290            cpp_leaf_fq(
8291                &scope.package_name,
8292                scope.class_unit.as_ref(),
8293                &name,
8294                SegmentKind::Nested,
8295                SegmentKind::Type,
8296            )
8297        };
8298        let code_unit = CodeUnit::with_signature_and_fq(
8299            self.file.clone(),
8300            CodeUnitType::Class,
8301            scope.package_name.clone(),
8302            short_name,
8303            scope.template_signature.clone(),
8304            false,
8305            fq,
8306        );
8307        let has_body = definition_body_present;
8308        if !has_body && self.parsed.contains_declaration(&code_unit) {
8309            if let Some(range) = explicit_range {
8310                self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8311            } else {
8312                self.link_source_declaration(ancestry, &code_unit, declaration_node);
8313            }
8314            self.parsed.record_navigation_range(
8315                code_unit.clone(),
8316                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8317            );
8318            return code_unit;
8319        }
8320        if has_body {
8321            // A forward declaration above this definition is one of the
8322            // class's declaration sites: C++ injects the class name at the
8323            // forward declaration, so a reference between the two spells this
8324            // class (#3297). Moving the unit to its body must not drop the
8325            // earlier site, exactly as a function prototype and its body stay
8326            // two ranges of one declaration (#1650).
8327            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8328            if let Some(range) = explicit_range {
8329                self.replace_declaration_with_range_deferred(
8330                    ancestry,
8331                    code_unit.clone(),
8332                    range,
8333                    Some(declaration_node),
8334                    None,
8335                    None,
8336                );
8337            } else {
8338                self.replace_declaration_deferred(
8339                    ancestry,
8340                    code_unit.clone(),
8341                    declaration_node,
8342                    None,
8343                    None,
8344                );
8345            }
8346            for range in earlier_ranges {
8347                self.parsed.add_declaration_range(&code_unit, range);
8348            }
8349        } else {
8350            self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8351        }
8352        if let Some(declaration) = self
8353            .source_links
8354            .get(&code_unit)
8355            .and_then(|links| links.last())
8356            .copied()
8357        {
8358            let mut properties = self.source_properties.borrow_mut();
8359            let fact = properties
8360                .get_mut(&declaration)
8361                .expect("linked class has source properties");
8362            fact.occurrence_role = if has_body {
8363                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8364            } else {
8365                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8366            };
8367            fact.class_strength = if has_body {
8368                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8369            } else {
8370                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8371            };
8372            if let Some(base_facts) = base_facts.as_ref() {
8373                fact.bases = base_facts.clone();
8374            }
8375        }
8376        if let Some(raw_supertypes) = raw_supertypes {
8377            self.parsed
8378                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8379        }
8380        self.parsed.add_signature(
8381            code_unit.clone(),
8382            render_cpp_type_signature(
8383                declaration_node,
8384                self.source,
8385                scope.template_signature.as_deref(),
8386            ),
8387        );
8388        if let Some(metadata) = &scope.template_metadata {
8389            let primary_short_name = if let Some(parent) = &scope.class_unit {
8390                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8391            } else {
8392                metadata.primary_name.clone()
8393            };
8394            let primary_fq_name = CodeUnit::new(
8395                self.file.clone(),
8396                CodeUnitType::Class,
8397                scope.package_name.clone(),
8398                primary_short_name,
8399            )
8400            .fq_name();
8401            let mut metadata = metadata.clone();
8402            metadata.primary_fq_name = primary_fq_name;
8403            self.parsed
8404                .set_cpp_template_metadata(code_unit.clone(), metadata);
8405        }
8406        if let Some(parent) = &scope.class_unit {
8407            self.parsed.add_child(parent.clone(), code_unit.clone());
8408        } else if let Some(block) = local_block {
8409            self.parsed
8410                .add_child(block.callable.clone(), code_unit.clone());
8411        } else if let Some(module) = &scope.module {
8412            self.parsed.add_child(module.clone(), code_unit.clone());
8413        }
8414
8415        if let Some(body) = body {
8416            let mut nested_scope = scope.clone();
8417            nested_scope.class_unit = Some(code_unit.clone());
8418            nested_scope.local_block = None;
8419            nested_scope.template_signature = scope.template_signature.clone();
8420            // Template metadata describes the class just created. It must not
8421            // leak into ordinary nested declarations in that class's body.
8422            // Recovered export-macro specializations carry a separate scope bit
8423            // for their declaration-shaped body members.
8424            nested_scope.template_metadata = None;
8425            // Export-macro class bodies recovered from a function_definition use
8426            // compound_statement children, whose direct fields are declarations.
8427            nested_scope.recovered_specialization_member_scope =
8428                scope.template_metadata.as_ref().is_some_and(|metadata| {
8429                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
8430                });
8431            nested_scope.declarations_are_fields =
8432                is_recovered_exported_class_container(declaration_node, self.source)
8433                    || nested_scope.recovered_specialization_member_scope;
8434            if let Some(displaced) = displaced_macro_tail {
8435                // A macro-shaped field without a source semicolon can make
8436                // tree-sitter consume the real class terminator as an ERROR
8437                // inside that field, then retain following namespace items as
8438                // later field-list children. Drain the proven class prefix
8439                // first and re-own only the structured tail with the outer
8440                // scope. The tail is pushed first because the work stack is
8441                // LIFO.
8442                push_cpp_sibling_range(
8443                    body,
8444                    displaced.split_index,
8445                    usize::MAX,
8446                    scope.clone(),
8447                    stack,
8448                );
8449                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8450            } else {
8451                stack.push(CppWork::Container(CppContainer {
8452                    node: body,
8453                    scope: nested_scope,
8454                }));
8455            }
8456        }
8457        if declaration_node.kind() == "enum_specifier" {
8458            self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8459        }
8460        code_unit
8461    }
8462
8463    fn visit_enum_enumerators(
8464        &mut self,
8465        ancestry: &ParentIndex<'_>,
8466        node: Node<'_>,
8467        scope: &ScopeInfo,
8468        parent: &CodeUnit,
8469    ) {
8470        walk_named_tree_preorder(node, false, |child| {
8471            if child.kind() != "enumerator" {
8472                return WalkControl::Continue;
8473            }
8474            let Some(name_node) = child.child_by_field_name("name") else {
8475                return WalkControl::Continue;
8476            };
8477            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8478            if name.is_empty() {
8479                return WalkControl::Continue;
8480            }
8481            let signature = normalize_cpp_whitespace(node_text(child, self.source));
8482            self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8483            WalkControl::Continue
8484        });
8485    }
8486
8487    fn visit_enum_enumerator(
8488        &mut self,
8489        ancestry: &ParentIndex<'_>,
8490        declaration_node: Node<'_>,
8491        scope: &ScopeInfo,
8492        parent: &CodeUnit,
8493        name: &str,
8494        signature: &str,
8495    ) {
8496        let code_unit = CodeUnit::new_fq(
8497            self.file.clone(),
8498            CodeUnitType::Field,
8499            scope.package_name.clone(),
8500            cpp_join_member_short(parent.short_name(), name),
8501            parent
8502                .fq()
8503                .clone()
8504                .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8505        );
8506        if self.parsed.contains_declaration(&code_unit) {
8507            return;
8508        }
8509        self.add_declaration(
8510            ancestry,
8511            code_unit.clone(),
8512            declaration_node,
8513            Some(parent.clone()),
8514            None,
8515        );
8516        self.parsed.add_signature(code_unit, signature.to_string());
8517    }
8518
8519    fn visit_function_definition<'tree>(
8520        &mut self,
8521        node: Node<'tree>,
8522        scope: &ScopeInfo,
8523        stack: &mut Vec<CppWork<'tree>>,
8524        ancestry: &ParentIndex<'tree>,
8525    ) {
8526        // An attribute-like macro invocation whose argument is a declaration can
8527        // swallow every declaration written after it into one bogus
8528        // `function_definition` (#2551). This owns the whole region, so it runs
8529        // ahead of `visit_macro_swallowed_function_declarations` below, which
8530        // admits the same envelope by its head macro token and reads single
8531        // declarators out of nodes this recovery reparses properly.
8532        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8533            return;
8534        }
8535        // A file-scope object-like macro sentinel the parser cannot see (issue
8536        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
8537        // prefixes as a bogus `function_definition` that swallows real namespaces,
8538        // classes, and members. Reparse the swallowed interior as C++ items so the
8539        // ordinary declaration visitors index it with byte/line-exact ownership.
8540        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8541            return;
8542        }
8543        // The omitted-semicolon field near miss (`DWORD preserved_field` ahead
8544        // of `void PreserveFollowingFunction() {}`): the macro-declaration
8545        // grammar folds the pair into the function's head as two leading
8546        // macro_modifiers where the older grammar left a damaged
8547        // field_declaration. Index the field; the function itself continues
8548        // through the ordinary path below.
8549        if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8550            self.visit_omitted_semicolon_macro_field(
8551                node,
8552                type_modifier,
8553                name_modifier,
8554                scope,
8555                ancestry,
8556            );
8557        }
8558        // The macro-return stand-in (`CUTLASS_DEVICE static OperandLayout
8559        // Layout() { ... }`): the same clean envelope, but the return type
8560        // names an active template type parameter, and the older grammar's
8561        // phantom field was the definition `OperandLayout::packed` resolved to
8562        // (#1129). Recover that field from the clean head.
8563        if scope.class_unit.is_some()
8564            && let Some((head_start, type_node)) =
8565                macro_return_template_parameter_field(node, self.source, ancestry)
8566        {
8567            self.visit_macro_return_template_parameter_field(
8568                node, head_start, type_node, scope, ancestry,
8569            );
8570        }
8571        if node.has_error() {
8572            self.visit_macro_swallowed_function_declarations(node, scope);
8573        }
8574        if let Some((class_node, name, raw_supertypes, base_facts)) =
8575            recover_exported_class_function_definition(node, self.source)
8576        {
8577            if let Some(body) = cpp_body_node(node)
8578                && let Some(close) = self
8579                    .orphaned_namespaces
8580                    .matching_close_brace(body.start_byte())
8581                && close.end_byte < body.end_byte()
8582            {
8583                let class_range = Range {
8584                    start_byte: node.start_byte(),
8585                    end_byte: close.end_byte,
8586                    start_line: node.start_position().row + 1,
8587                    end_line: close.end_line,
8588                };
8589                // Parse the body in class context, preserving the original
8590                // keyword, recovered name and body as included ranges. Parsing
8591                // the interior at file scope turns constructors and access
8592                // labels into another oversized ERROR instead of members.
8593                let mut head = vec![node];
8594                let mut keyword = None;
8595                let mut name_node = None;
8596                while let Some(part) = head.pop() {
8597                    if part.start_byte() >= body.start_byte() || part.is_missing() {
8598                        continue;
8599                    }
8600                    if matches!(part.kind(), "class" | "struct" | "union") {
8601                        keyword = Some(part);
8602                    }
8603                    if matches!(part.kind(), "identifier" | "type_identifier")
8604                        && node_text(part, self.source) == name
8605                    {
8606                        name_node = Some(part);
8607                    }
8608                    let mut cursor = part.walk();
8609                    head.extend(part.children(&mut cursor));
8610                }
8611                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8612                    && let Some(type_name) = keyword
8613                        .parent()
8614                        .and_then(|parent| parent.child_by_field_name("name"))
8615                    && let Some(tree) = parse_source_ranges_with_cancellation(
8616                        &tree_sitter_cpp::LANGUAGE.into(),
8617                        self.source,
8618                        &[
8619                            (keyword.start_byte(), type_name.start_byte()),
8620                            (name_node.start_byte(), name_node.end_byte()),
8621                            (body.start_byte(), close.end_byte),
8622                        ],
8623                        None,
8624                    )
8625                    && let Some(reparsed_class) = tree.root_node().named_child(0)
8626                    && let Some(class_body) = cpp_body_node(reparsed_class)
8627                    && class_body.start_byte() == body.start_byte()
8628                    && class_body.end_byte() == close.end_byte
8629                    && let Some(tail) =
8630                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8631                {
8632                    let class_body_range = class_body.byte_range();
8633                    self.with_recovery_source_context(node, |visitor| {
8634                        let source_context = visitor.active_source_context.clone();
8635                        let class_unit = visitor.visit_named_class_like_shape(
8636                            class_node,
8637                            name,
8638                            None,
8639                            true,
8640                            Some(class_range),
8641                            raw_supertypes,
8642                            base_facts,
8643                            scope,
8644                            stack,
8645                            ancestry,
8646                        );
8647                        visitor.parsed.record_materialization(
8648                            MaterializationRecord::RecoveredDeclaration {
8649                                recovery: class_range,
8650                                unit: class_unit.clone(),
8651                            },
8652                        );
8653                        let member_scope = ScopeInfo {
8654                            package_name: class_unit.package_name().to_string(),
8655                            class_unit: Some(class_unit),
8656                            declarations_are_fields: true,
8657                            template_metadata: None,
8658                            recovered_specialization_member_scope: false,
8659                            ..scope.clone()
8660                        };
8661                        let tail_range = tail.root_node().byte_range();
8662                        let class_body_recovery = cpp_recovery_window(
8663                            visitor.source,
8664                            class_body_range.start,
8665                            class_body_range.end,
8666                        );
8667                        let tail_recovery =
8668                            cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8669                        visitor.partitioned_regions.push(CppPartitionedRegion {
8670                            tree: tail,
8671                            range: tail_range,
8672                            scope: scope.clone(),
8673                            recovery: tail_recovery,
8674                            source_context: source_context.clone(),
8675                        });
8676                        visitor.partitioned_regions.push(CppPartitionedRegion {
8677                            tree,
8678                            range: class_body_range,
8679                            scope: member_scope,
8680                            recovery: class_body_recovery,
8681                            source_context: source_context.clone(),
8682                        });
8683                    });
8684                    return;
8685                }
8686            }
8687            let body = cpp_body_node(class_node);
8688            let displaced_namespace = cpp_body_node(node)
8689                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8690            let fragmented = cpp_body_node(node).and_then(|body| {
8691                fragmented_export_function_body_region(
8692                    node,
8693                    body,
8694                    self.source,
8695                    displaced_namespace.as_ref(),
8696                )
8697            });
8698            // The recovery tuple's first node is the class-like type when the
8699            // parser exposes one, but the synthetic wrapper owns the compound
8700            // statement that contains the truncated class body. Use the
8701            // wrapper body for fragmented-member detection; retain the
8702            // class-node body for the ordinary (non-fragmented) path below.
8703            if let Some(fragmented) = fragmented {
8704                // The lifted sibling no longer sits below the parser-visible
8705                // namespace node. Restore the current parent scope when the
8706                // ordinary work walk reaches that class.
8707                if let Some(boundary) =
8708                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8709                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8710                {
8711                    let mut boundary_scope = scope.clone();
8712                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8713                        if sibling.start_byte() >= boundary.start_byte() {
8714                            break;
8715                        }
8716                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8717                            boundary_scope.visible_using_namespaces.push(namespace);
8718                        }
8719                        push_cpp_using_declaration_binding(
8720                            &mut boundary_scope,
8721                            sibling,
8722                            self.source,
8723                        );
8724                    }
8725                    self.recovered_class_sibling_scopes
8726                        .insert(boundary.id(), boundary_scope);
8727                }
8728                let mut recovered_constructor = None;
8729                let mut recovered_prefix_tree = None;
8730                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8731                {
8732                    Some(FragmentedExportMembers::Complete(tree)) => {
8733                        if let Some(body) = body
8734                            && let Some(range) =
8735                                cpp_reparsed_synthetic_initializer_constructor_range(
8736                                    tree.root_node(),
8737                                    &name,
8738                                    self.source,
8739                                    body.end_byte(),
8740                                )
8741                        {
8742                            recovered_constructor = Some(range);
8743                            recovered_prefix_tree = Some(tree);
8744                            None
8745                        } else {
8746                            Some(FragmentedExportMembers::Complete(tree))
8747                        }
8748                    }
8749                    outcome => outcome,
8750                };
8751                let mut class_stack = Vec::new();
8752                let class_unit = self.visit_named_class_like_shape(
8753                    class_node,
8754                    name,
8755                    None,
8756                    true,
8757                    Some(fragmented.class_range),
8758                    raw_supertypes,
8759                    base_facts.clone(),
8760                    scope,
8761                    &mut class_stack,
8762                    ancestry,
8763                );
8764                self.parsed
8765                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
8766                        recovery: fragmented.class_range,
8767                        unit: class_unit.clone(),
8768                    });
8769                let complete = outcome.is_some_and(|outcome| {
8770                    self.visit_fragmented_export_class_members(
8771                        node,
8772                        outcome,
8773                        class_unit.clone(),
8774                        scope,
8775                    )
8776                });
8777                if complete {
8778                    self.consumed_fragment_regions
8779                        .push((node.start_byte(), fragmented.class_range.end_byte));
8780                } else {
8781                    // The reparse can fail when the first constructor or a
8782                    // method body is split into statement-shaped siblings.
8783                    // Keep the recovered class envelope, but do not visit the
8784                    // synthetic wrapper body: its initializer expressions can
8785                    // look like same-named member functions (for example
8786                    // `Token.location(loc)`). Re-own only the original sibling
8787                    // nodes that fall inside the proven class range. Their CST
8788                    // shapes retain the real field/function kinds and ranges.
8789                    let member_scope = ScopeInfo {
8790                        package_name: class_unit.package_name().to_string(),
8791                        module: scope.module.clone(),
8792                        class_unit: Some(class_unit.clone()),
8793                        template_signature: scope.template_signature.clone(),
8794                        template_metadata: None,
8795                        declarations_are_fields: true,
8796                        recovered_specialization_member_scope: false,
8797                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
8798                        local_block: None,
8799                        visible_using_declarations: scope.visible_using_declarations.clone(),
8800                    };
8801                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8802                        if candidate.start_byte() >= fragmented.reparse_end {
8803                            break;
8804                        }
8805                        if cpp_fragment_sibling_is_class_member(
8806                            candidate,
8807                            fragmented.reparse_end,
8808                            self.source,
8809                        ) {
8810                            self.recovered_class_sibling_scopes
8811                                .insert(candidate.id(), member_scope.clone());
8812                        }
8813                    }
8814                    if let Some(range) = recovered_constructor
8815                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8816                    {
8817                        let prefix_root = prefix_tree.root_node();
8818                        let prefix_ancestry = self.recovery_ancestry(prefix_root);
8819                        self.visit_recovered_fragment_prefix_members(
8820                            node,
8821                            prefix_root,
8822                            range.start,
8823                            &class_unit,
8824                            scope,
8825                            &prefix_ancestry,
8826                        );
8827                        self.visit_recovered_fragment_constructor(
8828                            range,
8829                            body,
8830                            class_node,
8831                            &class_unit,
8832                            scope,
8833                            ancestry,
8834                        );
8835                    }
8836                }
8837                if let Some(boundary) = displaced_namespace {
8838                    for item in boundary.namespace_items {
8839                        self.recovered_class_sibling_scopes
8840                            .insert(item.id(), scope.clone());
8841                    }
8842                }
8843                stack.extend(class_stack);
8844                return;
8845            }
8846            let mut stack = Vec::new();
8847            let class_unit = self.visit_named_class_like_shape(
8848                class_node,
8849                name,
8850                body,
8851                body.is_some(),
8852                None,
8853                raw_supertypes,
8854                base_facts,
8855                scope,
8856                &mut stack,
8857                ancestry,
8858            );
8859            self.parsed
8860                .record_materialization(MaterializationRecord::RecoveredDeclaration {
8861                    recovery: cpp_declaration_range(node),
8862                    unit: class_unit,
8863                });
8864            // Issue #1524: the bogus `function_definition` body can run past
8865            // the class's true closing brace (the parse ends it with a
8866            // zero-width `MISSING "}"`), swallowing following namespace-scope
8867            // siblings -- they would index as members of the recovered class.
8868            // When the body's text-balanced close lands before the body's own
8869            // end, re-own the swallowed tail with the outer scope instead.
8870            if let Some(body) = body
8871                && let Some(class_close) = self
8872                    .orphaned_namespaces
8873                    .matching_close_brace(body.start_byte())
8874                && class_close.start_byte < body.end_byte()
8875            {
8876                let split = {
8877                    let mut cursor = body.walk();
8878                    body.named_children(&mut cursor)
8879                        .position(|child| child.start_byte() > class_close.start_byte)
8880                };
8881                if let Some(split) = split {
8882                    // The seeded work is a single Container over the whole
8883                    // body with the class scope; replace it with the bounded
8884                    // head (class scope) plus the swallowed tail (outer
8885                    // scope). Push tail first so the head drains first.
8886                    let seeded = stack.pop();
8887                    match seeded {
8888                        Some(CppWork::Container(container)) => {
8889                            push_cpp_sibling_range(
8890                                body,
8891                                split,
8892                                usize::MAX,
8893                                scope.clone(),
8894                                &mut stack,
8895                            );
8896                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8897                        }
8898                        // visit_named_class_like_shape always seeds exactly
8899                        // one Container when a body is present.
8900                        _ => unreachable!("exported-class seed is always one Container"),
8901                    }
8902                }
8903            }
8904            while let Some(work) = stack.pop() {
8905                match work {
8906                    CppWork::Container(container) => {
8907                        push_cpp_container_work(container.node, container.scope, &mut stack);
8908                    }
8909                    CppWork::Siblings(siblings) => {
8910                        advance_cpp_siblings(siblings, self.source, &mut stack);
8911                    }
8912                    CppWork::Node(work) => {
8913                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8914                    }
8915                }
8916            }
8917            return;
8918        }
8919        let recovered_constraint_constructor =
8920            cpp_recovered_template_macro_constructor(node, self.source);
8921        // A constraint-macro template head the parser split off keeps the
8922        // function's declared type parameter, so the member stays a template.
8923        let constrained_scope;
8924        let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
8925            Some(parameter) => {
8926                constrained_scope = ScopeInfo {
8927                    template_signature: Some(format!(
8928                        "<{}>",
8929                        normalize_cpp_whitespace(node_text(parameter, self.source))
8930                    )),
8931                    ..scope.clone()
8932                };
8933                &constrained_scope
8934            }
8935            None => scope,
8936        };
8937        let declarator = recovered_constraint_constructor
8938            .map(|(declarator, _)| declarator)
8939            .or_else(|| {
8940                scope.class_unit.as_ref().and_then(|class| {
8941                    recovered_macro_decorated_constructor_declarator(
8942                        node,
8943                        class.identifier(),
8944                        self.source,
8945                    )
8946                })
8947            })
8948            .or_else(|| node.child_by_field_name("declarator"));
8949        let Some(declarator) = declarator else {
8950            self.visit_malformed_function_definition_container(node, scope, stack);
8951            return;
8952        };
8953        let Some(function_declarator) = extract_function_declarator(declarator) else {
8954            self.visit_malformed_function_definition_container(node, scope, stack);
8955            return;
8956        };
8957        let function = if let Some((_, callable_name)) =
8958            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8959        {
8960            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8961        } else {
8962            extract_function_info(function_declarator, self.source, scope)
8963        };
8964        let Some(mut function) = function else {
8965            self.visit_malformed_function_definition_container(node, scope, stack);
8966            return;
8967        };
8968        if let Some((_, template_parameter)) = recovered_constraint_constructor {
8969            function.signature = format!(
8970                "template <{}>{}",
8971                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8972                function.signature
8973            );
8974        }
8975        let code_unit = function.code_unit(self.file.clone());
8976        // Keep an earlier same-file prototype as another physical occurrence
8977        // of this callable. `CodeUnit` already identifies the role-neutral
8978        // overload, while ranges and signature metadata describe its
8979        // declaration/definition occurrences.
8980        self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8981        let signature = if recovered_constraint_constructor.is_some() {
8982            normalize_cpp_whitespace(node_text(function_declarator, self.source))
8983        } else {
8984            render_cpp_function_display_signature_from_node(
8985                node,
8986                self.source,
8987                scope.template_signature.as_deref(),
8988                true,
8989                ancestry,
8990            )
8991        };
8992        self.add_signature_with_metadata(
8993            code_unit.clone(),
8994            cpp_signature_metadata(
8995                signature,
8996                function_declarator,
8997                cpp_callable_linkage(node, self.source, ancestry),
8998                self.source,
8999                ancestry,
9000            )
9001            .with_declaration_only(false),
9002        );
9003        if let Some(parent) = &scope.class_unit {
9004            self.parsed.add_child(parent.clone(), code_unit.clone());
9005        } else if let Some(module) = &scope.module {
9006            self.parsed.add_child(module.clone(), code_unit.clone());
9007        }
9008        self.visit_local_types_in_function(node, &code_unit, scope, stack, ancestry);
9009    }
9010
9011    /// Recover the namespace lost when tree-sitter promotes an export-macro
9012    /// class definition to a root-level `function_definition`.  Only a
9013    /// body-bearing, top-level recovery may borrow a namespace, and only when
9014    /// one earlier namespace-scope forward declaration proves the identity.
9015    fn scope_for_recovered_exported_class<'tree>(
9016        &mut self,
9017        node: Node<'tree>,
9018        name: &str,
9019        definition_body_present: bool,
9020        scope: &ScopeInfo,
9021        ancestry: &ParentIndex<'tree>,
9022    ) -> ScopeInfo {
9023        if !definition_body_present
9024            || !scope.package_name.is_empty()
9025            || scope.class_unit.is_some()
9026            || !(is_recovered_exported_class_container(node, self.source)
9027                || recover_function_like_export_class_pair(node, self.source).is_some()
9028                || recover_embedded_function_like_export_classes(node, self.source)
9029                    .iter()
9030                    .any(|recovered| recovered.name == name)
9031                || matches!(node.kind(), "declaration" | "field_declaration")
9032                    && recover_exported_class_declaration(node, self.source).is_some()
9033                || matches!(
9034                    node.kind(),
9035                    "class_specifier" | "struct_specifier" | "union_specifier"
9036                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
9037                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
9038                        name_node,
9039                        self.source,
9040                    )))
9041                }) || ancestry.parent(node).is_some_and(|parent| {
9042                    matches!(parent.kind(), "declaration" | "field_declaration")
9043                        && recover_exported_class_declaration(parent, self.source).is_some()
9044                        || is_recovered_exported_class_container(parent, self.source)
9045                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
9046        {
9047            return scope.clone();
9048        }
9049        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
9050        let Some(package_name) = borrowed_namespace
9051            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
9052        else {
9053            return scope.clone();
9054        };
9055
9056        let module = CodeUnit::new_fq(
9057            self.file.clone(),
9058            CodeUnitType::Module,
9059            "",
9060            package_name.clone(),
9061            cpp_namespace_fq(&package_name),
9062        );
9063        let mut recovered = scope.clone();
9064        recovered.package_name = package_name;
9065        recovered.module = Some(module);
9066        recovered
9067    }
9068
9069    /// The unique namespace-scope forward declaration of `name` that precedes
9070    /// `recovered_node`, answered from the walk's carried-forward scan of the
9071    /// tree `recovered_node` belongs to.
9072    ///
9073    /// The scan is built on the first question and advanced by each later one,
9074    /// so a file that never reaches this path -- almost every file -- pays
9075    /// nothing, and one that reaches it thousands of times pays a single pass
9076    /// (#2754).
9077    fn unique_earlier_namespace_forward<'tree>(
9078        &mut self,
9079        recovered_node: Node<'tree>,
9080        name: &str,
9081        ancestry: &ParentIndex<'tree>,
9082    ) -> Option<String> {
9083        let mut root = recovered_node;
9084        while let Some(parent) = ancestry.parent(root) {
9085            root = parent;
9086        }
9087        let source = self.source;
9088        let scan = self
9089            .namespace_forward_scans
9090            .entry(CppTreeIdentity::of(root))
9091            .or_default();
9092        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
9093        let borrowed = scan.unique_earlier_forward(name, recovered_node);
9094
9095        #[cfg(debug_assertions)]
9096        assert_eq!(
9097            borrowed,
9098            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
9099            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
9100             for {name} at byte {}",
9101            recovered_node.start_byte()
9102        );
9103
9104        borrowed
9105    }
9106
9107    fn visit_malformed_function_definition_container<'tree>(
9108        &mut self,
9109        node: Node<'tree>,
9110        scope: &ScopeInfo,
9111        stack: &mut Vec<CppWork<'tree>>,
9112    ) {
9113        let Some(body) = cpp_body_node(node) else {
9114            return;
9115        };
9116        if !cpp_contains_namespace_definition(body) {
9117            return;
9118        }
9119        stack.push(CppWork::Container(CppContainer {
9120            node: body,
9121            scope: scope.clone(),
9122        }));
9123    }
9124
9125    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
9126    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
9127    /// emits for a sentinel-prefixed region, reparse the interior after the
9128    /// sentinel identifier as real C++ items -- confined to the region so
9129    /// every reparsed node keeps its original byte/line position -- and run the
9130    /// ordinary container visitation over the result. Returns `true` when it fired
9131    /// (the caller must then skip normal function processing). Nested sentinel
9132    /// regions recover recursively: the reparsed interior is walked through the
9133    /// same `visit_function_definition` path, so a sentinel inside the region hits
9134    /// this recovery again.
9135    /// Runs `reparse_walk` and records every declaration it mints as a
9136    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
9137    /// `recovery` (issue #1657). A reparsed sentinel region has no single
9138    /// recovered envelope unit: the ordinary visitors mint namespaces,
9139    /// classes, and members directly from the reparsed tree, so the walk's
9140    /// declaration delta is the recovered set. Records are ordered by
9141    /// declaration start byte so the parse product stays deterministic.
9142    fn record_recovered_declarations(
9143        &mut self,
9144        recovery: Range,
9145        origin: Node<'_>,
9146        reparse_walk: impl FnOnce(&mut Self),
9147    ) {
9148        // The recovered declarations begin at the recovery start, which can lie
9149        // past a branch directive the malformed origin swallowed (#6325:
9150        // `END_NS #else BEGIN_NS class C {};` parses as one declaration inside
9151        // the `#if` branch). Read their preprocessor context there.
9152        let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9153            && recovery.start_byte < origin.end_byte())
9154        .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9155        .flatten()
9156        .unwrap_or(origin);
9157        self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9158            let source_context = visitor.active_source_context.clone();
9159            visitor.record_recovered_declarations_with_context(
9160                recovery,
9161                source_context,
9162                reparse_walk,
9163            );
9164        });
9165    }
9166
9167    /// Run a recovered walk with an owned source context and record every
9168    /// declaration it mints. Deferred partitioned regions cannot retain their
9169    /// origin `Node`, so they capture the context while the origin tree is
9170    /// live and use this same materialization path when drained.
9171    fn record_recovered_declarations_with_context(
9172        &mut self,
9173        recovery: Range,
9174        source_context: Option<CppActiveSourceContext>,
9175        reparse_walk: impl FnOnce(&mut Self),
9176    ) {
9177        // The set difference this used to be, kept as the oracle every answer
9178        // is asserted against (#2787).
9179        #[cfg(any(debug_assertions, test))]
9180        let before = self.parsed.declarations().clone();
9181
9182        self.recovery_captures.push(CppRecoveryCapture::default());
9183        let previous = self.active_source_context.take();
9184        self.active_source_context = source_context;
9185        reparse_walk(self);
9186        self.active_source_context = previous;
9187        let captured = self
9188            .recovery_captures
9189            .pop()
9190            .expect("the capture this call pushed is the one it pops");
9191
9192        // The capture holds every declaration created while it was open, once
9193        // each and in creation order, so the recovered set costs what the
9194        // recovery made rather than everything the file has declared so far.
9195        // One filter is left to apply: a created declaration that a later
9196        // deferred replacement removed is not in the parse product to report.
9197        let mut minted: Vec<CodeUnit> = captured
9198            .created
9199            .into_iter()
9200            .filter(|unit| self.parsed.contains_declaration(unit))
9201            .collect();
9202        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9203
9204        #[cfg(any(debug_assertions, test))]
9205        {
9206            let mut rediscovered: Vec<CodeUnit> = self
9207                .parsed
9208                .declarations()
9209                .iter()
9210                .filter(|unit| !before.contains(*unit))
9211                .cloned()
9212                .collect();
9213            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9214            assert_eq!(
9215                minted, rediscovered,
9216                "the captured recovered set must be the declaration delta of the reparse \
9217                 walk over {recovery:?}"
9218            );
9219        }
9220
9221        for unit in minted {
9222            self.parsed
9223                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9224                    recovery,
9225                    unit,
9226                });
9227        }
9228    }
9229
9230    /// Where one recovered declaration sorts: by start byte, then by name, so
9231    /// the parse product stays deterministic.
9232    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9233        let start = self
9234            .parsed
9235            .declaration_ranges(unit)
9236            .first()
9237            .map(|range| range.start_byte)
9238            .unwrap_or(usize::MAX);
9239        (start, unit.fq_name().to_string())
9240    }
9241
9242    fn visit_sentinel_macro_region<'tree>(
9243        &mut self,
9244        node: Node<'tree>,
9245        scope: &ScopeInfo,
9246        stack: &mut Vec<CppWork<'tree>>,
9247        ancestry: &ParentIndex<'tree>,
9248    ) -> bool {
9249        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9250            return true;
9251        }
9252        if let Some((
9253            reparse_start,
9254            class_start,
9255            body_start,
9256            class_close_start,
9257            class_close_end,
9258            class_close_line,
9259        )) = cpp_sentinel_macro_class_region(node, self.source)
9260        {
9261            let Some(class_tree) =
9262                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9263            else {
9264                return false;
9265            };
9266            let class_root = class_tree.root_node();
9267            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9268            // A region reparse is its own tree and needs its own parent index.
9269            let class_ancestry = self.recovery_ancestry(class_root);
9270            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9271                class_root,
9272                template_node,
9273                self.source,
9274                &class_ancestry,
9275            ) else {
9276                return false;
9277            };
9278            let class_node = reparsed_class.declaration_node;
9279            let name = reparsed_class.name;
9280            let mut class_scope = scope.clone();
9281            if let Some(template_node) = template_node {
9282                class_scope.template_signature =
9283                    cpp_template_signature(template_node, class_node, self.source);
9284                class_scope.template_metadata =
9285                    cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9286            }
9287            let Some(body_tree) =
9288                cpp_reparse_region_items(self.source, body_start, class_close_start)
9289            else {
9290                return false;
9291            };
9292            let raw_supertypes = reparsed_class.raw_supertypes;
9293            let base_facts = reparsed_class.base_facts;
9294            let class_range = Range {
9295                start_byte: class_start,
9296                end_byte: class_close_end,
9297                start_line: class_node.start_position().row + 1,
9298                end_line: class_close_line,
9299            };
9300            let class_scope = self.scope_for_recovered_exported_class(
9301                class_node,
9302                &name,
9303                true,
9304                &class_scope,
9305                &class_ancestry,
9306            );
9307            let mut class_stack = Vec::new();
9308            self.with_recovery_source_context(node, |visitor| {
9309                let class_unit = visitor.visit_named_class_like_shape(
9310                    class_node,
9311                    name,
9312                    None,
9313                    true,
9314                    Some(class_range),
9315                    raw_supertypes,
9316                    base_facts,
9317                    &class_scope,
9318                    &mut class_stack,
9319                    &class_ancestry,
9320                );
9321                visitor.parsed.record_materialization(
9322                    MaterializationRecord::RecoveredDeclaration {
9323                        recovery: class_range,
9324                        unit: class_unit.clone(),
9325                    },
9326                );
9327                let member_scope = ScopeInfo {
9328                    package_name: class_scope.package_name.clone(),
9329                    module: class_scope.module.clone(),
9330                    class_unit: Some(class_unit),
9331                    template_signature: class_scope.template_signature.clone(),
9332                    template_metadata: None,
9333                    declarations_are_fields: true,
9334                    recovered_specialization_member_scope: false,
9335                    visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9336                    local_block: None,
9337                    visible_using_declarations: class_scope.visible_using_declarations.clone(),
9338                };
9339                // The padded body reparse is its own tree, so it indexes itself.
9340                let body_root = body_tree.root_node();
9341                visitor.run_container_work(
9342                    body_root,
9343                    member_scope,
9344                    &visitor.recovery_ancestry(body_root),
9345                );
9346            });
9347            // Register only after the padded body reparse: its nodes deliberately
9348            // retain offsets inside the consumed region and must be visited first.
9349            self.consumed_fragment_regions
9350                .push((node.start_byte(), class_close_end));
9351            // An ERROR envelope can hold real sibling declarations after the
9352            // recovered class's close (the suffix-reparse boundary in
9353            // `cpp_sentinel_macro_class_region` partitions, it does not
9354            // consume). Walk the envelope's remaining children normally; the
9355            // consumed region above keeps the recovered class from being
9356            // indexed twice.
9357            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9358                stack.push(CppWork::Container(CppContainer {
9359                    node,
9360                    scope: scope.clone(),
9361                }));
9362            }
9363            return true;
9364        }
9365        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9366            return false;
9367        };
9368        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9369            return false;
9370        };
9371        let root = tree.root_node();
9372        if !cpp_reparsed_items_are_indexable(root, self.source) {
9373            return false;
9374        }
9375        let recovery = cpp_recovery_window(self.source, start, end);
9376        // The reparsed region is its own tree, so this walk indexes it itself.
9377        let reparsed_ancestry = self.recovery_ancestry(root);
9378        self.record_recovered_declarations(recovery, node, |visitor| {
9379            visitor.visit_container(
9380                root,
9381                &reparsed_ancestry,
9382                &scope.package_name,
9383                scope.module.clone(),
9384                scope.class_unit.clone(),
9385                scope.template_signature.clone(),
9386                scope.visible_using_namespaces.clone(),
9387                scope.visible_using_declarations.clone(),
9388            );
9389        });
9390        if end > node.end_byte() {
9391            self.consumed_fragment_regions
9392                .push((node.start_byte(), end));
9393        } else if node.kind() == "ERROR" && node.end_byte() > end {
9394            // The sentinel region ended at the first recovered class-like item
9395            // but the ERROR envelope keeps real sibling declarations after it
9396            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
9397            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
9398            // envelope's remaining children normally; the consumed region
9399            // keeps the reparsed prefix from being indexed twice.
9400            self.consumed_fragment_regions
9401                .push((node.start_byte(), end));
9402            stack.push(CppWork::Container(CppContainer {
9403                node,
9404                scope: scope.clone(),
9405            }));
9406        }
9407        true
9408    }
9409
9410    /// Re-own complete class declarations from the structured Abseil
9411    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
9412    /// its direct CST children already prove both namespace components and the
9413    /// class bodies, so the ordinary class/member visitor can retain ownership
9414    /// and exact source ranges without admitting unrelated callable bodies.
9415    fn visit_nested_namespace_sentinel<'tree>(
9416        &mut self,
9417        node: Node<'tree>,
9418        scope: &ScopeInfo,
9419        ancestry: &ParentIndex<'tree>,
9420    ) -> bool {
9421        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9422            return false;
9423        };
9424        let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9425            recovered.function,
9426            &recovered.namespace_components,
9427            self.source,
9428        );
9429
9430        let mut package_name = scope.package_name.clone();
9431        let mut module = scope.module.clone();
9432        for component in recovered.namespace_components {
9433            package_name = if package_name.is_empty() {
9434                component
9435            } else {
9436                format!("{package_name}::{component}")
9437            };
9438            let namespace_module = CodeUnit::new_fq(
9439                self.file.clone(),
9440                CodeUnitType::Module,
9441                "",
9442                package_name.clone(),
9443                cpp_namespace_fq(&package_name),
9444            );
9445            if !self.parsed.contains_declaration(&namespace_module) {
9446                self.add_declaration(
9447                    ancestry,
9448                    namespace_module.clone(),
9449                    recovered.function,
9450                    None,
9451                    None,
9452                );
9453            }
9454            module = Some(namespace_module);
9455        }
9456
9457        let recovered_scope = ScopeInfo {
9458            package_name,
9459            module,
9460            // This scope's package comes from the namespace levels the sentinel
9461            // shape proves, so its owner must come from the same place. C++
9462            // never nests a namespace inside a class, so a surviving
9463            // `class_unit` here is recovery bleed from an earlier malformed
9464            // region, and keeping it would publish declarations whose package
9465            // names one lexical position and whose owner names another -- the
9466            // hybrid identity that trips the package/short boundary assert in
9467            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
9468            // identity-neutral for valid code, where `class_unit` is always
9469            // empty at a namespace.
9470            class_unit: None,
9471            template_signature: scope.template_signature.clone(),
9472            template_metadata: scope.template_metadata.clone(),
9473            declarations_are_fields: false,
9474            recovered_specialization_member_scope: false,
9475            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9476            local_block: None,
9477            visible_using_declarations: scope.visible_using_declarations.clone(),
9478        };
9479        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9480            recovered.function,
9481            recovered.body,
9482            self.source,
9483            ancestry,
9484        ) {
9485            let mut class_scope = recovered_scope.clone();
9486            if let Some(template_node) = fragmented.template_node {
9487                class_scope.template_signature =
9488                    cpp_template_signature(template_node, fragmented.class_node, self.source);
9489                class_scope.template_metadata = cpp_template_metadata(
9490                    template_node,
9491                    fragmented.class_node,
9492                    self.source,
9493                    ancestry,
9494                );
9495            }
9496            if let Some(outcome) = self
9497                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9498            {
9499                let mut class_stack = Vec::new();
9500                let class_unit = self.visit_named_class_like_shape(
9501                    fragmented.class_node,
9502                    fragmented.name.clone(),
9503                    None,
9504                    true,
9505                    Some(fragmented.fragmented.class_range),
9506                    fragmented.raw_supertypes.clone(),
9507                    Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9508                    &class_scope,
9509                    &mut class_stack,
9510                    ancestry,
9511                );
9512                self.parsed
9513                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
9514                        recovery: fragmented.fragmented.class_range,
9515                        unit: class_unit.clone(),
9516                    });
9517                let mut class_lexical_scope = sentinel_namespace.clone();
9518                class_lexical_scope.push(fragmented.name.clone());
9519                self.visit_sentinel_class_aliases(
9520                    cpp_sentinel_fragmented_class_aliases(&fragmented),
9521                    &class_unit,
9522                    &class_scope,
9523                    &class_lexical_scope,
9524                    ancestry,
9525                );
9526                if self.visit_fragmented_export_class_members(
9527                    node,
9528                    outcome,
9529                    class_unit,
9530                    &class_scope,
9531                ) {
9532                    self.consumed_fragment_regions.push((
9533                        fragmented.consumed_start,
9534                        fragmented.fragmented.class_range.end_byte,
9535                    ));
9536                }
9537            }
9538        }
9539        // The class requirement above is the admission gate; once admitted,
9540        // traverse the whole proven inner namespace body so sibling aliases,
9541        // functions, and variables are not silently discarded. The body is a
9542        // node of the tree being walked, so it reuses that tree's index.
9543        self.run_container_work(recovered.body, recovered_scope, ancestry);
9544        true
9545    }
9546
9547    /// Publish the original structured aliases of a recovered sentinel class
9548    /// as its members, then claim their byte ranges. The claim keeps the member
9549    /// reparse from publishing its statement-context misreading of the same
9550    /// text, and keeps the outer walk from publishing the alias again at
9551    /// namespace scope when the reparse leaves the class region unclaimed.
9552    ///
9553    /// The malformed CST ancestry names only the namespaces outside the
9554    /// sentinel, so the target type is read in `lexical_scope`, the recovered
9555    /// class scope the usage extractor also assigns to these nodes.
9556    fn visit_sentinel_class_aliases<'tree>(
9557        &mut self,
9558        aliases: Vec<Node<'tree>>,
9559        class_unit: &CodeUnit,
9560        scope: &ScopeInfo,
9561        lexical_scope: &[String],
9562        ancestry: &ParentIndex<'tree>,
9563    ) {
9564        let member_scope = ScopeInfo {
9565            package_name: class_unit.package_name().to_string(),
9566            module: scope.module.clone(),
9567            class_unit: Some(class_unit.clone()),
9568            template_signature: scope.template_signature.clone(),
9569            template_metadata: None,
9570            declarations_are_fields: true,
9571            recovered_specialization_member_scope: false,
9572            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9573            local_block: None,
9574            visible_using_declarations: scope.visible_using_declarations.clone(),
9575        };
9576        for alias in aliases {
9577            let mut alias_scope = member_scope.clone();
9578            let declaration = if alias.kind() == "template_declaration" {
9579                let declaration = alias
9580                    .named_children(&mut alias.walk())
9581                    .find(|child| child.kind() == "alias_declaration")
9582                    .expect("an admitted template alias holds its alias declaration");
9583                alias_scope.template_signature =
9584                    cpp_template_signature(alias, declaration, self.source);
9585                alias_scope.template_metadata =
9586                    cpp_template_metadata(alias, declaration, self.source, ancestry);
9587                declaration
9588            } else {
9589                alias
9590            };
9591            self.consumed_fragment_regions
9592                .push((alias.start_byte(), alias.end_byte()));
9593            let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9594                continue;
9595            };
9596            let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9597            let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9598            self.record_type_aliases(
9599                ancestry,
9600                declaration,
9601                &alias_scope,
9602                vec![name.clone()],
9603                signature,
9604                cpp_declaration_range(declaration),
9605                lexical_scope,
9606            );
9607            // The source fact reads its lexical path from the same malformed
9608            // ancestry; structural alias navigation joins this path.
9609            if let Some(declaration) = self
9610                .source_links
9611                .get(&unit)
9612                .and_then(|declarations| declarations.last())
9613            {
9614                let mut properties = self.source_properties.borrow_mut();
9615                let fact = properties
9616                    .get_mut(declaration)
9617                    .expect("a linked alias has a source fact");
9618                fact.lexical_path = lexical_scope.to_vec();
9619                fact.lexical_path.push(name);
9620            }
9621        }
9622    }
9623
9624    fn visit_declaration<'tree>(
9625        &mut self,
9626        node: Node<'tree>,
9627        scope: &ScopeInfo,
9628        in_class_body: bool,
9629        stack: &mut Vec<CppWork<'tree>>,
9630        ancestry: &ParentIndex<'tree>,
9631    ) {
9632        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9633            return;
9634        }
9635        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9636            return;
9637        }
9638        // Statement-context parsing treats Ctor(int value) as a parenthesized
9639        // variable declarator. Validate it as a class member before publishing
9640        // its parameter name as a field.
9641        if in_class_body
9642            && scope.class_unit.as_ref().is_some_and(|owner| {
9643                node.child_by_field_name("type").is_some_and(|ty| {
9644                    ty.kind() == "type_identifier"
9645                        && node_text(ty, self.source) == owner.identifier()
9646                })
9647            })
9648            && node
9649                .child_by_field_name("declarator")
9650                .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9651            && self.visit_bare_constructor_expression(node, scope, ancestry)
9652        {
9653            return;
9654        }
9655        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9656            !cpp_active_template_type_parameter(
9657                node,
9658                node_text(declarator, self.source),
9659                self.source,
9660                ancestry,
9661            )
9662        }) {
9663            return;
9664        }
9665        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9666            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
9667            // the following `Imaging image` member into one malformed field.
9668            // The ERROR's identifier is the actual declarator; route it
9669            // through the ordinary field path so its parent, range, and
9670            // signature metadata remain consistent with every other member.
9671            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9672            return;
9673        }
9674        if in_class_body
9675            && let Some(parent) = scope.class_unit.as_ref()
9676            && let Some(call) =
9677                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9678        {
9679            self.visit_recovered_macro_qualified_constructor_definition(
9680                node, call, scope, ancestry,
9681            );
9682            return;
9683        }
9684        if in_class_body
9685            && let Some(parent) = scope.class_unit.as_ref()
9686            && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9687                node,
9688                parent.identifier(),
9689                self.source,
9690            )
9691        {
9692            self.visit_recovered_macro_initialized_constructor_definition(
9693                node,
9694                constructor,
9695                scope,
9696                ancestry,
9697            );
9698            return;
9699        }
9700        if in_class_body
9701            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9702        {
9703            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9704            return;
9705        }
9706        if in_class_body
9707            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9708        {
9709            for member in members {
9710                self.add_macro_wrapped_declaration(member, scope, ancestry);
9711            }
9712            return;
9713        }
9714        if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9715            return;
9716        }
9717        if in_class_body
9718            && let Some(declarators) =
9719                recovered_macro_qualified_field_declarators(node, self.source)
9720        {
9721            for declarator in declarators {
9722                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9723            }
9724            return;
9725        }
9726        let recovered_alias_names = recovered_type_alias_names(node, self.source);
9727        if !recovered_alias_names.is_empty() {
9728            self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9729            return;
9730        }
9731        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9732            return;
9733        }
9734        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9735            return;
9736        }
9737
9738        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9739            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9740                // Issue #938: the members tree-sitter scattered out of the fragmented
9741                // multiple-base export node are reparsed from their true body region
9742                // and re-owned as members of the recovered class, with an explicit
9743                // navigation range spanning to the displaced closing brace.
9744                if let Some(outcome) =
9745                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9746                {
9747                    let consumed_region = (
9748                        recovered.declaration_node.end_byte(),
9749                        fragmented.class_range.end_byte,
9750                    );
9751                    let code_unit = self.visit_named_class_like_shape(
9752                        recovered.declaration_node,
9753                        recovered.name,
9754                        None,
9755                        true,
9756                        Some(fragmented.class_range),
9757                        recovered.raw_supertypes,
9758                        recovered.base_facts,
9759                        scope,
9760                        stack,
9761                        ancestry,
9762                    );
9763                    self.parsed.record_materialization(
9764                        MaterializationRecord::RecoveredDeclaration {
9765                            recovery: fragmented.class_range,
9766                            unit: code_unit.clone(),
9767                        },
9768                    );
9769                    let consume_fragment =
9770                        self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9771                    // Everything between the fragmented declaration and its displaced
9772                    // closing brace now belongs to the recovered class; keep the
9773                    // ordinary walk from re-indexing those scattered siblings at top
9774                    // level. Register the consumed region only after indexing because
9775                    // the reparsed nodes retain byte offsets inside that same region.
9776                    if consume_fragment {
9777                        self.consumed_fragment_regions.push(consumed_region);
9778                    }
9779                    return;
9780                }
9781            }
9782            let uses_initializer_body = recovered.uses_initializer_body;
9783            let definition_body_present = recovered.body.is_some();
9784            let class_unit = self.visit_named_class_like_shape(
9785                recovered.declaration_node,
9786                recovered.name,
9787                recovered.body,
9788                definition_body_present,
9789                None,
9790                recovered.raw_supertypes,
9791                recovered.base_facts,
9792                scope,
9793                stack,
9794                ancestry,
9795            );
9796            self.parsed
9797                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9798                    recovery: cpp_declaration_range(node),
9799                    unit: class_unit,
9800                });
9801            if uses_initializer_body {
9802                return;
9803            }
9804        }
9805
9806        // A function-like macro invocation that expands to declarations keeps
9807        // no terminator of its own: the semicolons live in the replacement
9808        // body, which is not part of this tree. Tree-sitter recovers one and
9809        // reads the macro name as the member type and its argument list as a
9810        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
9811        // published a field named `ClosedDetect` beside the real constructor
9812        // and split every reference to the constructor's name between the two
9813        // (#3298). The invented terminator is the whole difference from a real
9814        // member: a member terminates itself, and with a terminator the
9815        // grammar reads `T (name);` as a declaration rather than a field.
9816        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9817            return;
9818        }
9819
9820        let mut handled_function = false;
9821        let mut handled_declarator = false;
9822        let mut cursor = node.walk();
9823        for child in node.named_children(&mut cursor) {
9824            if matches!(
9825                child.kind(),
9826                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9827            ) {
9828                // A named class-like definition remains a declaration even when
9829                // the same statement also declares an object, for example
9830                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
9831                // declaration's type and `kind` as its declarator.  Dropping the
9832                // type here loses both its nested owner and every later lexical
9833                // reference to it.  A body is the structured proof that this is
9834                // a definition rather than an elaborated type use such as
9835                // `class Kind value;`.
9836                if cpp_body_node(child).is_some() {
9837                    self.visit_class_like(child, scope, stack, ancestry);
9838                }
9839                continue;
9840            }
9841        }
9842
9843        let mut cursor = node.walk();
9844        for child in node.children_by_field_name("declarator", &mut cursor) {
9845            if crate::structural::is_recovered_designator_init_declarator(child) {
9846                handled_declarator = true;
9847                continue;
9848            }
9849            if in_class_body
9850                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9851            {
9852                handled_declarator = true;
9853                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9854                continue;
9855            }
9856            if let Some(kind) = classify_declarator(child) {
9857                handled_declarator = true;
9858                match kind {
9859                    DeclaratorKind::Function(function_declarator) => {
9860                        handled_function = true;
9861                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9862                    }
9863                    DeclaratorKind::Variable(variable_declarator) => {
9864                        self.visit_variable_declaration(
9865                            node,
9866                            variable_declarator,
9867                            scope,
9868                            in_class_body,
9869                            ancestry,
9870                        );
9871                    }
9872                }
9873            }
9874        }
9875
9876        if !handled_declarator {
9877            let mut cursor = node.walk();
9878            for child in node.named_children(&mut cursor) {
9879                if crate::structural::is_recovered_designator_init_declarator(child) {
9880                    handled_declarator = true;
9881                    continue;
9882                }
9883                if !is_unfielded_declarator_candidate(child) {
9884                    continue;
9885                }
9886                let Some(kind) = classify_declarator(child) else {
9887                    continue;
9888                };
9889                handled_declarator = true;
9890                match kind {
9891                    DeclaratorKind::Function(function_declarator) => {
9892                        handled_function = true;
9893                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9894                    }
9895                    DeclaratorKind::Variable(variable_declarator) => {
9896                        self.visit_variable_declaration(
9897                            node,
9898                            variable_declarator,
9899                            scope,
9900                            in_class_body,
9901                            ancestry,
9902                        );
9903                    }
9904                }
9905            }
9906        }
9907
9908        if handled_function {
9909            return;
9910        }
9911
9912        if !handled_declarator {
9913            if in_class_body {
9914                self.visit_class_members_from_declaration(node, scope, ancestry);
9915            } else {
9916                self.visit_global_variables_from_declaration(node, scope, ancestry);
9917            }
9918        }
9919    }
9920
9921    /// Preserve the member structure of an anonymous aggregate.
9922    ///
9923    /// An anonymous union with no declarator promotes its fields into the
9924    /// containing aggregate. An anonymous struct/union followed by a named
9925    /// declarator, such as `struct { T *ops; } sock`, declares both the field
9926    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
9927    /// the declarator's structured nested identity so a later `value.sock.ops`
9928    /// chain can traverse it without parsing a type spelling (#2407).
9929    ///
9930    /// An anonymous aggregate declares no tag, so there is nothing for the C
9931    /// dialect to re-scope and nothing for the C++ dialect to nest: both
9932    /// readings of this shape are the same, and both need these members
9933    /// (#3301).
9934    fn anonymous_aggregate_declaration_parts<'tree>(
9935        node: Node<'tree>,
9936    ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9937        let aggregate = node.child_by_field_name("type")?;
9938        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9939            || aggregate.child_by_field_name("name").is_some()
9940        {
9941            return None;
9942        }
9943        let body = cpp_body_node(aggregate)?;
9944        let mut cursor = node.walk();
9945        let declarators = node
9946            .children_by_field_name("declarator", &mut cursor)
9947            .filter_map(|declarator| match classify_declarator(declarator) {
9948                Some(DeclaratorKind::Variable(variable)) => Some(variable),
9949                Some(DeclaratorKind::Function(_)) | None => None,
9950            })
9951            .collect();
9952        Some((aggregate, body, declarators))
9953    }
9954
9955    fn visit_anonymous_aggregate_declaration<'tree>(
9956        &mut self,
9957        node: Node<'tree>,
9958        scope: &ScopeInfo,
9959        in_class_body: bool,
9960        stack: &mut Vec<CppWork<'tree>>,
9961        ancestry: &ParentIndex<'tree>,
9962    ) -> bool {
9963        if !in_class_body || scope.class_unit.is_none() {
9964            return false;
9965        }
9966        let Some((aggregate, body, declarators)) =
9967            Self::anonymous_aggregate_declaration_parts(node)
9968        else {
9969            return false;
9970        };
9971        if declarators.is_empty() {
9972            stack.push(CppWork::Container(CppContainer {
9973                node: body,
9974                scope: scope.clone(),
9975            }));
9976            return true;
9977        }
9978
9979        for declarator in declarators {
9980            let Some(name) = extract_variable_name(declarator, self.source) else {
9981                continue;
9982            };
9983            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9984            // The receiver type's identity is the declarator's name, so its
9985            // range is the field declaration that carries that name token.
9986            // The inner specifier ends at the closing brace, which leaves the
9987            // name the unit is known by outside its own text (#3287).
9988            self.visit_named_class_like_shape(
9989                aggregate,
9990                name,
9991                Some(body),
9992                true,
9993                Some(cpp_declaration_range(node)),
9994                None,
9995                None,
9996                scope,
9997                stack,
9998                ancestry,
9999            );
10000        }
10001        true
10002    }
10003
10004    /// Give a function-local anonymous aggregate a structured owner so its
10005    /// direct pointer bindings can be typed by the usage graph.  Unlike an
10006    /// anonymous aggregate in a class body, there is no source-level tag or
10007    /// typedef to supply an identity.  The aggregate's CST range is therefore
10008    /// part of a generated, collision-resistant identity; no source spelling
10009    /// 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 name = format!("<anonymous:{}>", aggregate.start_byte());
10035        self.visit_named_class_like_shape(
10036            aggregate,
10037            name,
10038            Some(body),
10039            true,
10040            None,
10041            None,
10042            None,
10043            scope,
10044            stack,
10045            ancestry,
10046        );
10047        true
10048    }
10049
10050    fn visit_function_declaration<'tree>(
10051        &mut self,
10052        declaration_node: Node<'tree>,
10053        declarator: Node<'tree>,
10054        scope: &ScopeInfo,
10055        ancestry: &ParentIndex<'tree>,
10056    ) {
10057        let Some(function) = extract_function_info(declarator, self.source, scope) else {
10058            return;
10059        };
10060        // An in-class member prototype is a real source declaration, not a
10061        // generated or recovery-only unit, so it keeps the ordinary code-unit
10062        // identity (#3508). #3493 recorded its callable modifiers, which is
10063        // what a canonical procedure key needs; a synthetic unit would still
10064        // refuse that key on identity grounds alone.
10065        let code_unit = function.code_unit(self.file.clone());
10066        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10067            return;
10068        }
10069        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10070        let signature = render_cpp_function_display_signature_from_node(
10071            declaration_node,
10072            self.source,
10073            scope.template_signature.as_deref(),
10074            false,
10075            ancestry,
10076        );
10077        self.add_signature_with_metadata(
10078            code_unit.clone(),
10079            cpp_signature_metadata(
10080                signature,
10081                declarator,
10082                cpp_callable_linkage(declaration_node, self.source, ancestry),
10083                self.source,
10084                ancestry,
10085            )
10086            .with_declaration_only(true),
10087        );
10088        if let Some(parent) = &scope.class_unit {
10089            self.parsed.add_child(parent.clone(), code_unit);
10090        } else if let Some(module) = &scope.module {
10091            self.parsed.add_child(module.clone(), code_unit);
10092        }
10093    }
10094
10095    fn link_existing_source_declaration(
10096        &mut self,
10097        ancestry: &ParentIndex<'_>,
10098        code_unit: &CodeUnit,
10099        declaration_node: Node<'_>,
10100    ) -> bool {
10101        if !self.parsed.contains_declaration(code_unit) {
10102            return false;
10103        }
10104        self.link_source_declaration(ancestry, code_unit, declaration_node);
10105        self.parsed
10106            .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
10107        true
10108    }
10109
10110    fn visit_recovered_macro_qualified_function_declaration<'tree>(
10111        &mut self,
10112        declaration_node: Node<'tree>,
10113        call: Node<'tree>,
10114        scope: &ScopeInfo,
10115        ancestry: &ParentIndex<'tree>,
10116    ) {
10117        let Some(parent) = &scope.class_unit else {
10118            return;
10119        };
10120        let Some(name_node) = call.child_by_field_name("function") else {
10121            return;
10122        };
10123        let Some(arguments) = call.child_by_field_name("arguments") else {
10124            return;
10125        };
10126        let Some((signature, parameter_labels)) =
10127            recovered_macro_qualified_function_parameters(arguments, self.source)
10128        else {
10129            return;
10130        };
10131        let arity = parameter_labels.len();
10132        let function = FunctionInfo {
10133            package_name: scope.package_name.clone(),
10134            owner: Some(CppMemberOwner::Unit(parent.clone())),
10135            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10136            signature,
10137        };
10138        if function.name.is_empty() {
10139            return;
10140        }
10141        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10142        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10143            return;
10144        }
10145        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10146        let signature_label = render_cpp_function_display_signature_from_node(
10147            declaration_node,
10148            self.source,
10149            scope.template_signature.as_deref(),
10150            false,
10151            ancestry,
10152        );
10153        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10154        // Only the callable's parameters came from the reparse: the
10155        // `field_declaration` this recovery reads is a real node in a real
10156        // class body, so its storage class and the body's access ladder state
10157        // this member's modifiers exactly as an ordinary member's do.
10158        let modifiers =
10159            cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10160        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10161            .with_declaration_only(true)
10162            .with_callable_arity(CallableArity::exact(arity))
10163            .with_callable_linkage(linkage)
10164            .with_callable_modifiers(
10165                modifiers.is_static,
10166                modifiers.is_constructor,
10167                modifiers.visibility,
10168            );
10169        self.add_signature_with_metadata(code_unit.clone(), metadata);
10170        self.parsed.add_child(parent.clone(), code_unit);
10171    }
10172
10173    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10174        &mut self,
10175        declaration_node: Node<'tree>,
10176        call: Node<'tree>,
10177        scope: &ScopeInfo,
10178        ancestry: &ParentIndex<'tree>,
10179    ) {
10180        let Some(parent) = &scope.class_unit else {
10181            return;
10182        };
10183        let Some(arguments) = call.child_by_field_name("arguments") else {
10184            return;
10185        };
10186        let Some((mut signature, parameter_labels)) =
10187            recovered_macro_qualified_function_parameters(arguments, self.source)
10188        else {
10189            return;
10190        };
10191        if let Some(template_signature) = &scope.template_signature {
10192            signature = format!("{template_signature}{signature}");
10193        }
10194        let arity = parameter_labels.len();
10195        let function = FunctionInfo {
10196            package_name: scope.package_name.clone(),
10197            owner: Some(CppMemberOwner::Unit(parent.clone())),
10198            name: parent.identifier().to_string(),
10199            signature,
10200        };
10201        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10202        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10203        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10204        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10205        // The recovery already proved this member is the class's constructor:
10206        // the reparsed call names the enclosing class. Everything else is read
10207        // from the real `field_declaration` the recovery found it in.
10208        let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10209        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10210            .with_declaration_only(false)
10211            .with_callable_arity(CallableArity::exact(arity))
10212            .with_callable_linkage(linkage)
10213            .with_callable_modifiers(
10214                modifiers.is_static,
10215                modifiers.is_constructor,
10216                modifiers.visibility,
10217            );
10218        self.add_signature_with_metadata(code_unit.clone(), metadata);
10219        self.parsed.add_child(parent.clone(), code_unit);
10220    }
10221
10222    /// Publish the constructor of the macro-decorated field shape that
10223    /// `recovered_macro_initialized_constructor_declarator` proves. The head
10224    /// is a real, clean function declarator, so it keeps the ordinary
10225    /// identity and metadata; the `default_value` initializer list is the
10226    /// constructor body, so the occurrence is a definition.
10227    fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10228        &mut self,
10229        declaration_node: Node<'tree>,
10230        function_declarator: Node<'tree>,
10231        scope: &ScopeInfo,
10232        ancestry: &ParentIndex<'tree>,
10233    ) {
10234        let Some(parent) = &scope.class_unit else {
10235            return;
10236        };
10237        let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10238            return;
10239        };
10240        let code_unit = function.code_unit(self.file.clone());
10241        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10242        // Render the head the way an ordinary constructor definition renders
10243        // the text ahead of its body, member initializers included.
10244        let body_start = declaration_node
10245            .child_by_field_name("default_value")
10246            .map_or(declaration_node.end_byte(), |body| body.start_byte());
10247        let head =
10248            collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10249        let signature = match scope.template_signature.as_deref() {
10250            Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10251            None => format!("{head} {{...}}"),
10252        };
10253        self.add_signature_with_metadata(
10254            code_unit.clone(),
10255            cpp_signature_metadata(
10256                signature,
10257                function_declarator,
10258                cpp_callable_linkage(declaration_node, self.source, ancestry),
10259                self.source,
10260                ancestry,
10261            )
10262            .with_declaration_only(false),
10263        );
10264        self.parsed.add_child(parent.clone(), code_unit);
10265    }
10266
10267    fn visit_variable_declaration<'tree>(
10268        &mut self,
10269        declaration_node: Node<'tree>,
10270        declarator: Node<'tree>,
10271        scope: &ScopeInfo,
10272        in_class_body: bool,
10273        ancestry: &ParentIndex<'tree>,
10274    ) {
10275        let Some(name) = extract_variable_name(declarator, self.source) else {
10276            return;
10277        };
10278        let parent = if in_class_body {
10279            let Some(parent) = &scope.class_unit else {
10280                return;
10281            };
10282            Some(parent)
10283        } else {
10284            None
10285        };
10286        let short_name = match parent {
10287            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10288            None => name.clone(),
10289        };
10290        let fq = cpp_leaf_fq(
10291            &scope.package_name,
10292            parent,
10293            &name,
10294            SegmentKind::Member,
10295            SegmentKind::Member,
10296        );
10297        let code_unit = CodeUnit::new_fq(
10298            self.file.clone(),
10299            CodeUnitType::Field,
10300            scope.package_name.clone(),
10301            short_name,
10302            fq,
10303        );
10304        if self.parsed.contains_declaration(&code_unit) {
10305            return;
10306        }
10307        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10308        self.add_signature_with_metadata(
10309            code_unit.clone(),
10310            SignatureMetadata::new(
10311                render_cpp_field_signature(declaration_node, declarator, self.source),
10312                Vec::new(),
10313            )
10314            .with_cpp_field_linkage(cpp_field_declaration_linkage(
10315                declaration_node,
10316                self.source,
10317                ancestry,
10318            )),
10319        );
10320        if let Some(parent) = &scope.class_unit {
10321            self.parsed.add_child(parent.clone(), code_unit);
10322        } else if let Some(module) = &scope.module {
10323            self.parsed.add_child(module.clone(), code_unit);
10324        }
10325    }
10326
10327    /// Index the field half of an omitted-semicolon pair the macro-declaration
10328    /// grammar folded into the following function definition's head. The
10329    /// function itself continues through the ordinary visitor path.
10330    fn visit_omitted_semicolon_macro_field<'tree>(
10331        &mut self,
10332        node: Node<'tree>,
10333        type_modifier: Node<'tree>,
10334        name_modifier: Node<'tree>,
10335        scope: &ScopeInfo,
10336        ancestry: &ParentIndex<'tree>,
10337    ) {
10338        let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10339            return;
10340        };
10341        let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10342        if name.is_empty() {
10343            return;
10344        }
10345        let range = Range {
10346            start_byte: type_modifier.start_byte(),
10347            end_byte: name_modifier.end_byte(),
10348            start_line: type_modifier.start_position().row + 1,
10349            end_line: name_modifier.end_position().row + 1,
10350        };
10351        self.record_macro_head_field(node, range, name, scope, ancestry);
10352    }
10353
10354    /// Index the macro-return stand-in field: a clean function definition whose
10355    /// macro-modified head returns an active template type parameter. The field
10356    /// is named after the return type and spans the head up to it, exactly the
10357    /// phantom `field_declaration` the older grammar emitted (#1129).
10358    fn visit_macro_return_template_parameter_field<'tree>(
10359        &mut self,
10360        node: Node<'tree>,
10361        head_start: Node<'tree>,
10362        type_node: Node<'tree>,
10363        scope: &ScopeInfo,
10364        ancestry: &ParentIndex<'tree>,
10365    ) {
10366        let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10367        let range = Range {
10368            start_byte: head_start.start_byte(),
10369            end_byte: type_node.end_byte(),
10370            start_line: head_start.start_position().row + 1,
10371            end_line: type_node.end_position().row + 1,
10372        };
10373        self.record_macro_head_field(node, range, name, scope, ancestry);
10374    }
10375
10376    /// Record one field recovered from a function definition's macro head. The
10377    /// signature is the head's own text: like the older grammar's phantom
10378    /// field_declaration, it carries no semicolon because none was written.
10379    fn record_macro_head_field<'tree>(
10380        &mut self,
10381        node: Node<'tree>,
10382        range: Range,
10383        name: String,
10384        scope: &ScopeInfo,
10385        ancestry: &ParentIndex<'tree>,
10386    ) {
10387        if name.is_empty() {
10388            return;
10389        }
10390        let parent = scope.class_unit.as_ref();
10391        let short_name = match parent {
10392            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10393            None => name.clone(),
10394        };
10395        let code_unit = CodeUnit::new_fq(
10396            self.file.clone(),
10397            CodeUnitType::Field,
10398            scope.package_name.clone(),
10399            short_name,
10400            cpp_leaf_fq(
10401                &scope.package_name,
10402                parent,
10403                &name,
10404                SegmentKind::Member,
10405                SegmentKind::Member,
10406            ),
10407        );
10408        if self.parsed.contains_declaration(&code_unit) {
10409            return;
10410        }
10411        self.add_declaration_with_range(
10412            ancestry,
10413            code_unit.clone(),
10414            range,
10415            Some(node),
10416            parent.cloned(),
10417            None,
10418        );
10419        let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10420        self.add_signature_with_metadata(
10421            code_unit.clone(),
10422            SignatureMetadata::new(signature, Vec::new())
10423                .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10424        );
10425        if let Some(parent) = &scope.class_unit {
10426            self.parsed.add_child(parent.clone(), code_unit);
10427        } else if let Some(module) = &scope.module {
10428            self.parsed.add_child(module.clone(), code_unit);
10429        }
10430    }
10431
10432    fn variable_declarator_from_declaration_child<'tree>(
10433        child: Node<'tree>,
10434    ) -> Option<Node<'tree>> {
10435        if child.kind() == "init_declarator" {
10436            child.child_by_field_name("declarator")
10437        } else if matches!(
10438            child.kind(),
10439            "identifier"
10440                | "field_identifier"
10441                | "pointer_declarator"
10442                | "reference_declarator"
10443                | "array_declarator"
10444                | "parenthesized_declarator"
10445                | "macro_modifier_declarator"
10446        ) {
10447            Some(child)
10448        } else {
10449            None
10450        }
10451    }
10452
10453    fn visit_class_members_from_declaration<'tree>(
10454        &mut self,
10455        node: Node<'tree>,
10456        scope: &ScopeInfo,
10457        ancestry: &ParentIndex<'tree>,
10458    ) {
10459        let mut cursor = node.walk();
10460        for child in node.named_children(&mut cursor) {
10461            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10462                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10463            } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10464            {
10465                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10466            }
10467        }
10468    }
10469
10470    fn visit_global_variables_from_declaration<'tree>(
10471        &mut self,
10472        node: Node<'tree>,
10473        scope: &ScopeInfo,
10474        ancestry: &ParentIndex<'tree>,
10475    ) {
10476        let mut cursor = node.walk();
10477        for child in node.named_children(&mut cursor) {
10478            if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10479                self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10480            }
10481        }
10482    }
10483
10484    fn visit_type_declaration<'tree>(
10485        &mut self,
10486        node: Node<'tree>,
10487        scope: &ScopeInfo,
10488        stack: &mut Vec<CppWork<'tree>>,
10489        ancestry: &ParentIndex<'tree>,
10490    ) {
10491        let type_node = node.child_by_field_name("type");
10492        if let Some(type_node) = type_node
10493            && matches!(
10494                type_node.kind(),
10495                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10496            )
10497        {
10498            self.visit_class_like(type_node, scope, stack, ancestry);
10499        }
10500
10501        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10502            let range = Range {
10503                start_byte: node.start_byte(),
10504                end_byte: recovered.end_node.end_byte(),
10505                start_line: node.start_position().row + 1,
10506                end_line: recovered.end_node.end_position().row + 1,
10507            };
10508            let signature = self
10509                .source
10510                .get(range.start_byte..range.end_byte)
10511                .map(normalize_cpp_whitespace)
10512                .unwrap_or_default();
10513            self.record_type_aliases(
10514                ancestry,
10515                node,
10516                scope,
10517                vec![recovered.name],
10518                signature,
10519                range,
10520                &cpp_callable_lexical_scope(node, self.source, ancestry),
10521            );
10522            return;
10523        }
10524
10525        let alias_names = match node.kind() {
10526            "alias_declaration" => extract_alias_declaration_name(node, self.source)
10527                .into_iter()
10528                .collect::<Vec<_>>(),
10529            "type_definition" => extract_typedef_alias_names(node, self.source),
10530            _ => Vec::new(),
10531        };
10532        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10533            (type_node, alias_names.as_slice())
10534            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10535            && type_node.child_by_field_name("name").is_none()
10536        {
10537            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10538        } else {
10539            None
10540        };
10541        self.add_type_aliases(ancestry, node, scope, alias_names);
10542        if let Some((body, alias_name)) = anonymous_aggregate {
10543            // The typedef alias is also the only user-visible identity of an
10544            // anonymous aggregate. Reuse it as the member owner instead of
10545            // minting a second signatureless class with the same FQN. The
10546            // latter makes forward lookup ambiguous when conditional aliases
10547            // coexist and returns duplicate definitions even without guards.
10548            let signature = normalize_cpp_whitespace(node_text(node, self.source));
10549            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10550            debug_assert!(self.parsed.contains_declaration(&alias_unit));
10551            let mut nested_scope = scope.clone();
10552            nested_scope.class_unit = Some(alias_unit);
10553            nested_scope.template_signature = scope.template_signature.clone();
10554            nested_scope.template_metadata = None;
10555            nested_scope.declarations_are_fields = false;
10556            nested_scope.recovered_specialization_member_scope = false;
10557            stack.push(CppWork::Container(CppContainer {
10558                node: body,
10559                scope: nested_scope,
10560            }));
10561        }
10562    }
10563
10564    fn add_type_aliases(
10565        &mut self,
10566        ancestry: &ParentIndex<'_>,
10567        node: Node<'_>,
10568        scope: &ScopeInfo,
10569        alias_names: Vec<String>,
10570    ) {
10571        let signature = normalize_cpp_whitespace(node_text(node, self.source));
10572        self.record_type_aliases(
10573            ancestry,
10574            node,
10575            scope,
10576            alias_names,
10577            signature,
10578            cpp_declaration_range(node),
10579            &cpp_callable_lexical_scope(node, self.source, ancestry),
10580        );
10581    }
10582
10583    /// `lexical_scope` is where the alias target's names are looked up: the
10584    /// alias's CST ancestry, or the owner a structural recovery proved when
10585    /// the CST ancestry is malformed.
10586    #[allow(clippy::too_many_arguments)]
10587    fn record_type_aliases(
10588        &mut self,
10589        ancestry: &ParentIndex<'_>,
10590        node: Node<'_>,
10591        scope: &ScopeInfo,
10592        alias_names: Vec<String>,
10593        signature: String,
10594        range: Range,
10595        lexical_scope: &[String],
10596    ) {
10597        if signature.is_empty() {
10598            return;
10599        }
10600        let type_name = node
10601            .child_by_field_name("type")
10602            .and_then(|type_node| type_node.child_by_field_name("name"))
10603            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10604        for alias_name in alias_names {
10605            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10606                continue;
10607            }
10608            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10609            // Declaration identity does not include the alias signature. Keep
10610            // each physical range so conditional aliases retain their guards.
10611            self.note_declaration(&code_unit);
10612            self.link_source_declaration(ancestry, &code_unit, node);
10613            self.parsed
10614                .add_code_unit_with_range(code_unit.clone(), range, None, None);
10615            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10616                cpp_structured_type_identity(type_node, self.source, lexical_scope)
10617            });
10618            self.add_signature_with_metadata(
10619                code_unit.clone(),
10620                SignatureMetadata::new(signature.clone(), Vec::new())
10621                    .with_underlying_type_identity(underlying_type_identity),
10622            );
10623            if let Some(metadata) = &scope.template_metadata {
10624                let mut metadata = metadata.clone();
10625                metadata.primary_fq_name = code_unit.fq_name();
10626                self.parsed
10627                    .set_cpp_template_metadata(code_unit.clone(), metadata);
10628            }
10629            if let Some(parent) = &scope.class_unit {
10630                self.parsed.add_child(parent.clone(), code_unit.clone());
10631            } else if let Some(module) = &scope.module {
10632                self.parsed.add_child(module.clone(), code_unit.clone());
10633            }
10634            self.parsed.mark_type_alias(code_unit);
10635        }
10636    }
10637
10638    fn type_alias_unit(
10639        &self,
10640        scope: &ScopeInfo,
10641        alias_name: String,
10642        signature: String,
10643    ) -> CodeUnit {
10644        let short_name = if let Some(parent) = &scope.class_unit {
10645            cpp_join_nested_short(parent.short_name(), &alias_name)
10646        } else {
10647            alias_name.clone()
10648        };
10649        let fq = cpp_leaf_fq(
10650            &scope.package_name,
10651            scope.class_unit.as_ref(),
10652            &alias_name,
10653            SegmentKind::Nested,
10654            SegmentKind::Type,
10655        );
10656        CodeUnit::with_signature_and_fq(
10657            self.file.clone(),
10658            CodeUnitType::Class,
10659            scope.package_name.clone(),
10660            short_name,
10661            Some(signature),
10662            false,
10663            fq,
10664        )
10665    }
10666
10667    fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10668        // A comment can terminate tree-sitter's preproc_arg before the logical
10669        // directive ends. Its remaining declarations are replacement locals,
10670        // not file-scope fields, even when recovery exposes them as siblings.
10671        if let Some(replacement) =
10672            crate::graph::syntax::function_macro_replacement_span(node, self.source)
10673        {
10674            self.consumed_fragment_regions
10675                .push((replacement.start, replacement.end));
10676        }
10677        let Some(name) = extract_macro_name(node, self.source) else {
10678            return;
10679        };
10680        let signature = node_text(node, self.source).trim_end().to_string();
10681        if signature.is_empty() {
10682            return;
10683        }
10684        let fq = cpp_member_fq("", &name);
10685        // A macro can be undefined and redefined later in the same file. Its
10686        // structured directive is part of the declaration identity so the
10687        // temporal environment can navigate to the definition active at a
10688        // reference instead of collapsing every spelling to the first range.
10689        // The same physical directive parsed through another C/C++ reading
10690        // still produces the same unit and remains deduplicated.
10691        let code_unit = CodeUnit::with_signature_and_fq(
10692            self.file.clone(),
10693            CodeUnitType::Macro,
10694            "",
10695            name.clone(),
10696            Some(signature.clone()),
10697            false,
10698            fq,
10699        );
10700        if !self.parsed.contains_declaration(&code_unit) {
10701            self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10702            let name_range = node
10703                .child_by_field_name("name")
10704                .map(cpp_declaration_range)
10705                .unwrap_or_else(|| cpp_declaration_range(node));
10706            self.parsed
10707                .record_materialization(MaterializationRecord::GeneratedDeclaration {
10708                    site: cpp_declaration_range(node),
10709                    argument: name_range,
10710                    kind: GenerationKind::PreprocessorDefinition,
10711                    unit: code_unit.clone(),
10712                });
10713            self.parsed.add_signature(code_unit, signature);
10714        }
10715        if node.kind() == "preproc_def" {
10716            update_object_macro_field_environment(
10717                node,
10718                self.source,
10719                &mut self.object_macro_fields,
10720                &mut self.ambiguous_object_macro_fields,
10721            );
10722        } else {
10723            self.object_macro_fields.remove(&name);
10724            self.ambiguous_object_macro_fields.remove(&name);
10725        }
10726    }
10727
10728    /// Bare object-like field-list macros inside an otherwise well-formed
10729    /// aggregate remain identifier nodes in a field declaration. More than
10730    /// one adjacent invocation can be folded into the same declaration, so
10731    /// inspect all of its structured identifier nodes.
10732    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10733        if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10734            return false;
10735        }
10736        // Claiming the declaration means its members are accounted for, which
10737        // is only true when there is an owner to materialize them into. A file
10738        // scope reaches this with a declaration that merely *contains* an
10739        // aggregate spelling an invocation (tree-sitter wraps libuv's
10740        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
10741        // `field_declaration` under a malformed header), and consuming it there
10742        // would discard the aggregate itself (#2985).
10743        if scope.class_unit.is_none() {
10744            return false;
10745        }
10746        let macro_nodes =
10747            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10748        for macro_node in &macro_nodes {
10749            let name = node_text(*macro_node, self.source).trim();
10750            let fields = object_macro_field_closure(&self.object_macro_fields, name);
10751            self.materialize_object_macro_fields(
10752                fields,
10753                cpp_declaration_range(*macro_node),
10754                *macro_node,
10755                scope,
10756            );
10757        }
10758        let Some(last) = macro_nodes.last() else {
10759            return false;
10760        };
10761        // Claiming the declaration accounts for every member it spells, not
10762        // only the donated ones: an invocation folded together with the
10763        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
10764        // leaves those members shapeless, and the grammar recovers them from
10765        // the byte range that follows the invocation.
10766        self.record_collapsed_aggregate_fields(
10767            last.end_byte()..node.end_byte(),
10768            node.start_position().row
10769                + 1
10770                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10771            node,
10772            scope,
10773        );
10774        true
10775    }
10776
10777    /// Retain the grammar's field interpretation with the original invocation
10778    /// or aggregate context. The replacement AST owns the type, while only the
10779    /// original source owns activation, guards, and occurrence ranges.
10780    fn capture_recovered_field_facts(
10781        &mut self,
10782        unit: &CodeUnit,
10783        range: Range,
10784        context: Node<'_>,
10785        field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10786        names_function_type: bool,
10787    ) {
10788        let declaration = *self.source_links[unit]
10789            .last()
10790            .expect("recovered field follows its source occurrence");
10791        let mut fact = crate::source_facts::capture_declaration(
10792            declaration,
10793            context,
10794            unit,
10795            self.source,
10796            self.primary_ancestry,
10797        );
10798        fact.field_type = field_type;
10799        fact.names_function_type = names_function_type;
10800        fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10801        let captured = crate::source_context::capture_declaration_context(
10802            context,
10803            unit,
10804            self.source,
10805            &mut self.preprocessor_context_cache,
10806        );
10807        fact.guard_requirements = captured.guard_requirements;
10808        fact.callable_guards = fact.guard_requirements.clone();
10809        fact.callable_activation = Some(range.end_byte);
10810        fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10811        fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10812        let intern = |node: Node<'_>| {
10813            let mut collector = self.source_collector.borrow_mut();
10814            if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10815                collector.intern_node(node)
10816            } else {
10817                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10818            }
10819        };
10820        fact.conditional_family =
10821            crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10822        fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10823        fact.displaced_namespace_closing_brace =
10824            captured.displaced_namespace_closing_brace.map(intern);
10825        self.capture_source_dependencies(&mut fact, context, unit);
10826        self.apply_active_source_context(&mut fact);
10827        assert!(
10828            self.source_properties
10829                .borrow_mut()
10830                .insert(declaration, fact)
10831                .is_none()
10832        );
10833    }
10834
10835    fn materialize_object_macro_fields(
10836        &mut self,
10837        fields: Vec<MacroReplacementField>,
10838        range: Range,
10839        context: Node<'_>,
10840        scope: &ScopeInfo,
10841    ) {
10842        let Some(owner) = scope.class_unit.as_ref() else {
10843            return;
10844        };
10845        for field in fields {
10846            let signature = field.declaration.clone();
10847            let mut fq = owner.fq().clone();
10848            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10849            let short_name = if owner.short_name().is_empty() {
10850                field.name.clone()
10851            } else {
10852                format!("{}.{}", owner.short_name(), field.name)
10853            };
10854            let code_unit = CodeUnit::with_signature_and_fq(
10855                self.file.clone(),
10856                CodeUnitType::Field,
10857                owner.package_name().to_string(),
10858                short_name,
10859                Some(field.declaration),
10860                true,
10861                fq,
10862            );
10863            if self.parsed.contains_declaration(&code_unit) {
10864                continue;
10865            }
10866            self.add_declaration_with_range(
10867                self.primary_ancestry,
10868                code_unit.clone(),
10869                range,
10870                None,
10871                Some(owner.clone()),
10872                None,
10873            );
10874            self.capture_recovered_field_facts(
10875                &code_unit,
10876                range,
10877                context,
10878                field.field_type,
10879                field.names_function_type,
10880            );
10881            self.parsed.add_signature(code_unit, signature);
10882        }
10883    }
10884
10885    fn collapsed_aggregate_owner(
10886        &self,
10887        name: &str,
10888        scope: &ScopeInfo,
10889        parent: Option<&CodeUnit>,
10890    ) -> CodeUnit {
10891        let short_name = parent.map_or_else(
10892            || name.to_string(),
10893            |parent| cpp_join_nested_short(parent.short_name(), name),
10894        );
10895        let fq = cpp_leaf_fq(
10896            &scope.package_name,
10897            parent,
10898            name,
10899            SegmentKind::Nested,
10900            SegmentKind::Type,
10901        );
10902        CodeUnit::with_signature_and_fq(
10903            self.file.clone(),
10904            CodeUnitType::Class,
10905            scope.package_name.clone(),
10906            short_name,
10907            None,
10908            false,
10909            fq,
10910        )
10911    }
10912
10913    /// Recover ordinary aggregates whose field-list macro invocations made
10914    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
10915    /// The class markers, aggregate braces, field nodes, and macro identifier
10916    /// nodes are all read from the AST; no source delimiter or token scan is
10917    /// used. A close-brace node inside a malformed field closes the innermost
10918    /// recovered aggregate, preserving nested-owner identity.
10919    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10920        let mut cursor = node.walk();
10921        let children = node.children(&mut cursor).collect::<Vec<_>>();
10922        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10923        let mut object_macro_fields = self.object_macro_fields.clone();
10924        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10925        let mut open = Vec::<usize>::new();
10926        let mut index = 0;
10927        while index < children.len() {
10928            let keyword = children[index];
10929            if update_object_macro_field_environment(
10930                keyword,
10931                self.source,
10932                &mut object_macro_fields,
10933                &mut ambiguous_object_macro_fields,
10934            ) {
10935                index += 1;
10936                continue;
10937            }
10938            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10939                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10940                if !name.is_empty() {
10941                    let parent = open
10942                        .last()
10943                        .and_then(|class| recovered.get(*class))
10944                        .map(|(owner, _, _, _)| owner.clone())
10945                        .or_else(|| scope.class_unit.clone());
10946                    let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10947                    recovered.push((
10948                        owner,
10949                        head.key.start_byte(),
10950                        head.opening.end_byte(),
10951                        Vec::new(),
10952                    ));
10953                    let class_index = recovered.len() - 1;
10954                    match head.folded_members {
10955                        // The ordinary shape leaves the aggregate open: every
10956                        // child that follows belongs to it until a close brace
10957                        // ends it.
10958                        None => open.push(class_index),
10959                        // A folded head carries its own member list and its own
10960                        // closing brace, so it closes here. The invocations that
10961                        // precede the class key are the dangling tail of the
10962                        // replacement that folded it, and belong to whatever
10963                        // aggregate is open around it, not to this one.
10964                        Some(members) => {
10965                            let macro_nodes = object_macro_identifier_nodes_with_environment(
10966                                children[index],
10967                                self.source,
10968                                &mut object_macro_fields,
10969                                &mut ambiguous_object_macro_fields,
10970                            );
10971                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10972                                .iter()
10973                                .partition(|node| node.start_byte() < head.key.start_byte());
10974                            if let Some(&enclosing) = open.last() {
10975                                for macro_node in preceding {
10976                                    recovered[enclosing]
10977                                        .3
10978                                        .push(CppCollapsedMember::MacroFields {
10979                                            context: macro_node,
10980                                            range: cpp_declaration_range(macro_node),
10981                                            fields: object_macro_field_closure(
10982                                                &object_macro_fields,
10983                                                &normalize_cpp_whitespace(node_text(
10984                                                    macro_node,
10985                                                    self.source,
10986                                                )),
10987                                            ),
10988                                        });
10989                                }
10990                            }
10991                            let closing = cpp_collapsed_aggregate_closing_brace(members);
10992                            recovered[class_index]
10993                                .3
10994                                .extend(cpp_collapsed_aggregate_members(
10995                                    members,
10996                                    &inner,
10997                                    head.opening.end_byte()..closing,
10998                                    head.opening.end_position().row + 1,
10999                                    self.source,
11000                                    &object_macro_fields,
11001                                ));
11002                            recovered[class_index].2 = members.end_byte();
11003                            for &open_class in &open {
11004                                recovered[open_class].2 =
11005                                    recovered[open_class].2.max(members.end_byte());
11006                            }
11007                        }
11008                    }
11009                    index += head.width;
11010                    continue;
11011                }
11012            }
11013            if let Some(&class_index) = open.last()
11014                && children[index].kind() == "field_declaration"
11015            {
11016                let field = children[index];
11017                let macro_nodes = object_macro_identifier_nodes_with_environment(
11018                    field,
11019                    self.source,
11020                    &mut object_macro_fields,
11021                    &mut ambiguous_object_macro_fields,
11022                );
11023                recovered[class_index]
11024                    .3
11025                    .extend(cpp_collapsed_aggregate_members(
11026                        field,
11027                        &macro_nodes,
11028                        field.start_byte()..field.end_byte(),
11029                        field.start_position().row + 1,
11030                        self.source,
11031                        &object_macro_fields,
11032                    ));
11033                let end = field.end_byte();
11034                for &open_class in &open {
11035                    recovered[open_class].2 = recovered[open_class].2.max(end);
11036                }
11037                let closes = count_close_brace_nodes(field);
11038                for _ in 0..closes {
11039                    if let Some(closed) = open.pop() {
11040                        recovered[closed].2 = end;
11041                    }
11042                }
11043            }
11044            index += 1;
11045        }
11046
11047        let mut owners = Vec::with_capacity(recovered.len());
11048        for (owner, start, end, members) in recovered {
11049            let parent = owners
11050                .iter()
11051                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
11052                .cloned()
11053                .or_else(|| scope.class_unit.clone());
11054            self.declare_collapsed_aggregate(
11055                owner.clone(),
11056                start..end,
11057                Some(node),
11058                parent,
11059                members,
11060                scope,
11061            );
11062            owners.push(owner);
11063        }
11064    }
11065
11066    /// Record one recovered aggregate and the members it declares.
11067    fn declare_collapsed_aggregate(
11068        &mut self,
11069        owner: CodeUnit,
11070        span: std::ops::Range<usize>,
11071        evidence: Option<Node<'_>>,
11072        parent: Option<CodeUnit>,
11073        members: Vec<CppCollapsedMember<'_>>,
11074        scope: &ScopeInfo,
11075    ) {
11076        let range = Range {
11077            start_byte: span.start,
11078            end_byte: span.end,
11079            start_line: self.source.get(..span.start).map_or(1, |source| {
11080                source.bytes().filter(|byte| *byte == b'\n').count() + 1
11081            }),
11082            end_line: self.source.get(..span.end).map_or(1, |source| {
11083                source.bytes().filter(|byte| *byte == b'\n').count() + 1
11084            }),
11085        };
11086        // The tag can already be declared by a typedef or a forward
11087        // declaration, and this range is the definition's. Record it either
11088        // way: a declaration carries every range it is written at, and
11089        // dropping this one leaves nothing able to prove that the aggregate
11090        // has a body (#3098). Recording a range that is already held, or a
11091        // unit that is already declared, changes nothing.
11092        self.add_declaration_with_range(
11093            self.primary_ancestry,
11094            owner.clone(),
11095            range,
11096            evidence,
11097            parent,
11098            None,
11099        );
11100        // The recovery admitted this owner from an aggregate head and body.
11101        // Its malformed outer node need not itself be a struct_specifier, so
11102        // retain that structured definition proof explicitly.
11103        let declaration = *self.source_links[&owner]
11104            .last()
11105            .expect("collapsed aggregate has a source occurrence");
11106        {
11107            let mut properties = self.source_properties.borrow_mut();
11108            let fact = properties
11109                .get_mut(&declaration)
11110                .expect("collapsed aggregate retains its original context");
11111            fact.class_strength =
11112                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11113            fact.occurrence_role =
11114                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11115        }
11116        let owner_scope = ScopeInfo {
11117            class_unit: Some(owner),
11118            declarations_are_fields: true,
11119            ..scope.clone()
11120        };
11121        for member in members {
11122            match member {
11123                CppCollapsedMember::MacroFields {
11124                    range,
11125                    fields,
11126                    context,
11127                } => {
11128                    self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11129                }
11130                CppCollapsedMember::Declarations {
11131                    span,
11132                    start_line,
11133                    context,
11134                } => {
11135                    self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11136                }
11137            }
11138        }
11139    }
11140
11141    /// Recover an aggregate whose head tree-sitter folded into the declaration
11142    /// that precedes it, outside any collapsed `ERROR` container.
11143    ///
11144    /// A comment inside a field-list macro's replacement ends the replacement
11145    /// token, so the rest of the replacement is read at file scope. When the
11146    /// aggregate that follows declares its member list with one invocation,
11147    /// the parser reads the dangling tail as that declaration's type, the
11148    /// class key as an `ERROR`, and the member list as an initializer (#3098).
11149    /// The invocations before the class key belong to the replacement, not to
11150    /// this aggregate, so they go to whatever aggregate encloses it.
11151    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11152        if node.parent().is_some_and(|parent| parent.is_error()) {
11153            // The container's own recovery reads this child in source order,
11154            // with the preprocessor environment its earlier children build.
11155            return false;
11156        }
11157        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11158            return false;
11159        };
11160        let members = head
11161            .folded_members
11162            .expect("a folded aggregate head carries its member list");
11163        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11164        if name.is_empty() {
11165            return false;
11166        }
11167        let macro_nodes =
11168            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11169        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11170            .iter()
11171            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11172        for macro_node in preceding {
11173            let fields = object_macro_field_closure(
11174                &self.object_macro_fields,
11175                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11176            );
11177            self.materialize_object_macro_fields(
11178                fields,
11179                cpp_declaration_range(macro_node),
11180                macro_node,
11181                scope,
11182            );
11183        }
11184        let parent = scope.class_unit.clone();
11185        let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11186        let recovered = cpp_collapsed_aggregate_members(
11187            members,
11188            &inner,
11189            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11190            head.opening.end_position().row + 1,
11191            self.source,
11192            &self.object_macro_fields,
11193        );
11194        self.declare_collapsed_aggregate(
11195            owner,
11196            head.key.start_byte()..members.end_byte(),
11197            Some(node),
11198            parent,
11199            recovered,
11200            scope,
11201        );
11202        true
11203    }
11204
11205    /// Record the members of a collapsed aggregate region as fields of its
11206    /// recovered owner. The names, declaration text, and byte ranges all come
11207    /// from the grammar's reading of the region, so a region that is not a
11208    /// member list contributes nothing.
11209    fn record_collapsed_aggregate_fields(
11210        &mut self,
11211        span: std::ops::Range<usize>,
11212        start_line: usize,
11213        context: Node<'_>,
11214        scope: &ScopeInfo,
11215    ) {
11216        let Some(owner) = scope.class_unit.as_ref() else {
11217            return;
11218        };
11219        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11220            let range = Range {
11221                start_byte: field.range.start,
11222                end_byte: field.range.end,
11223                start_line: start_line
11224                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
11225                end_line: start_line
11226                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
11227            };
11228            let mut fq = owner.fq().clone();
11229            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11230            let short_name = if owner.short_name().is_empty() {
11231                field.name.clone()
11232            } else {
11233                format!("{}.{}", owner.short_name(), field.name)
11234            };
11235            let signature = normalize_cpp_whitespace(&field.declaration);
11236            let code_unit = CodeUnit::with_signature_and_fq(
11237                self.file.clone(),
11238                CodeUnitType::Field,
11239                owner.package_name().to_string(),
11240                short_name,
11241                Some(signature.clone()),
11242                false,
11243                fq,
11244            );
11245            if self.parsed.contains_declaration(&code_unit) {
11246                continue;
11247            }
11248            self.add_declaration_with_range(
11249                self.primary_ancestry,
11250                code_unit.clone(),
11251                range,
11252                None,
11253                Some(owner.clone()),
11254                None,
11255            );
11256            self.capture_recovered_field_facts(
11257                &code_unit,
11258                range,
11259                context,
11260                field.field_type,
11261                field.names_function_type,
11262            );
11263            self.parsed.add_signature(code_unit, signature);
11264        }
11265    }
11266
11267    fn visit_preproc_call(&mut self, node: Node<'_>) {
11268        let Some(_directive) = node.child_by_field_name("directive") else {
11269            return;
11270        };
11271        if is_cpp_undef_directive(node, self.source) {
11272            update_object_macro_field_environment(
11273                node,
11274                self.source,
11275                &mut self.object_macro_fields,
11276                &mut self.ambiguous_object_macro_fields,
11277            );
11278        }
11279    }
11280}
11281
11282/// Every member a field-list macro contributes under `environment`, including
11283/// the members of the field-list macros it composes.
11284///
11285/// Composition is resolved at the invocation, not at the definition, because a
11286/// nested name's active replacement is a property of the environment the
11287/// invocation sees. The traversal is iterative and visits each name once, so a
11288/// self-composing or mutually composing pair terminates.
11289fn object_macro_field_closure(
11290    environment: &HashMap<String, ObjectMacroReplacement>,
11291    name: &str,
11292) -> Vec<MacroReplacementField> {
11293    let mut fields = Vec::new();
11294    let mut visited = HashSet::default();
11295    let mut stack = vec![name.to_string()];
11296    while let Some(current) = stack.pop() {
11297        if !visited.insert(current.clone()) {
11298            continue;
11299        }
11300        let Some(replacement) = environment.get(&current) else {
11301            continue;
11302        };
11303        fields.extend(replacement.fields.iter().cloned());
11304        stack.extend(replacement.nested.iter().rev().cloned());
11305    }
11306    fields
11307}
11308
11309/// The structured replacement an object-like `#define` contributes.
11310///
11311/// The directive's whole logical line is the replacement list, which
11312/// [`object_macro_replacement_span`] recovers, because a comment inside the
11313/// replacement ends tree-sitter's `preproc_arg` token early.
11314fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11315    crate::graph::syntax::object_macro_replacement_span(node, source)
11316        .and_then(|span| source.get(span))
11317        .map(crate::graph::syntax::object_macro_replacement)
11318        .unwrap_or_default()
11319}
11320
11321/// Apply one preprocessor directive to an object-like field-list environment.
11322/// The environment is intentionally separate from the declaration visitor so
11323/// malformed parser regions can replay directives in source order without
11324/// mutating the live walk's state ahead of those directives.
11325fn update_object_macro_field_environment(
11326    node: Node<'_>,
11327    source: &str,
11328    fields: &mut HashMap<String, ObjectMacroReplacement>,
11329    ambiguous: &mut HashSet<String>,
11330) -> bool {
11331    match node.kind() {
11332        "preproc_def" => {
11333            let Some(name) = extract_macro_name(node, source) else {
11334                return false;
11335            };
11336            let replacement = object_macro_replacement_of(node, source);
11337            if replacement.is_empty() || ambiguous.contains(&name) {
11338                fields.remove(&name);
11339                ambiguous.insert(name);
11340            } else if let Some(previous) = fields.get(&name) {
11341                if previous != &replacement {
11342                    fields.remove(&name);
11343                    ambiguous.insert(name);
11344                }
11345            } else {
11346                fields.insert(name, replacement);
11347            }
11348            true
11349        }
11350        "preproc_call" if is_cpp_undef_directive(node, source) => {
11351            if let Some(argument) = node.child_by_field_name("argument") {
11352                let name = node_text(argument, source).trim();
11353                fields.remove(name);
11354                if inside_preprocessor_conditional(node) {
11355                    ambiguous.insert(name.to_string());
11356                } else {
11357                    ambiguous.remove(name);
11358                }
11359            }
11360            true
11361        }
11362        _ => false,
11363    }
11364}
11365
11366fn object_macro_identifier_nodes<'tree>(
11367    node: Node<'tree>,
11368    source: &str,
11369    fields: &HashMap<String, ObjectMacroReplacement>,
11370) -> Vec<Node<'tree>> {
11371    walk_object_macro_identifier_nodes(node, |current, result| {
11372        if matches!(
11373            current.kind(),
11374            "identifier" | "field_identifier" | "type_identifier"
11375        ) && fields.contains_key(node_text(current, source).trim())
11376        {
11377            result.push(current);
11378        }
11379        false
11380    })
11381}
11382
11383fn walk_object_macro_identifier_nodes<'tree>(
11384    node: Node<'tree>,
11385    mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11386) -> Vec<Node<'tree>> {
11387    let mut result = Vec::new();
11388    let mut stack = vec![node];
11389    while let Some(current) = stack.pop() {
11390        if visit(current, &mut result) {
11391            continue;
11392        }
11393        let mut cursor = current.walk();
11394        let children = current.children(&mut cursor).collect::<Vec<_>>();
11395        stack.extend(children.into_iter().rev());
11396    }
11397    result.sort_by_key(|node| node.start_byte());
11398    result
11399}
11400
11401/// Collect macro identifiers while replaying any nested preprocessor
11402/// directives in source order. Recovery regions can contain a later `#undef`
11403/// in the same parser error envelope; using the live visitor map for the
11404/// entire envelope would incorrectly materialize invocations after it.
11405fn object_macro_identifier_nodes_with_environment<'tree>(
11406    node: Node<'tree>,
11407    source: &str,
11408    fields: &mut HashMap<String, ObjectMacroReplacement>,
11409    ambiguous: &mut HashSet<String>,
11410) -> Vec<Node<'tree>> {
11411    walk_object_macro_identifier_nodes(node, |current, result| {
11412        if update_object_macro_field_environment(current, source, fields, ambiguous) {
11413            return true;
11414        }
11415        if matches!(
11416            current.kind(),
11417            "identifier" | "field_identifier" | "type_identifier"
11418        ) && fields.contains_key(node_text(current, source).trim())
11419        {
11420            result.push(current);
11421        }
11422        false
11423    })
11424}
11425
11426/// One member region of an aggregate whose head and body tree-sitter collapsed
11427/// into an `ERROR` container, in source order.
11428enum CppCollapsedMember<'tree> {
11429    /// The members a field-list macro invocation donates, all sharing the
11430    /// invocation's range.
11431    MacroFields {
11432        context: Node<'tree>,
11433        range: Range,
11434        fields: Vec<MacroReplacementField>,
11435    },
11436    /// A byte span whose ordinary member declarations lost their shape with
11437    /// the aggregate body, with the 1-based line the span starts on.
11438    Declarations {
11439        context: Node<'tree>,
11440        span: std::ops::Range<usize>,
11441        start_line: usize,
11442    },
11443}
11444
11445/// One aggregate head recovered from the children of a collapsed `ERROR`
11446/// container.
11447///
11448/// A member list that opens with an object-like field-list macro invocation
11449/// has no grammar rule, so the aggregate never forms a specifier and its head
11450/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
11451/// class key, the name, and the opening brace are three consecutive children.
11452/// When the aggregate follows the dangling tail of a comment-truncated
11453/// replacement, the parser reads that tail as the declaration's type and folds
11454/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
11455/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
11456/// }` that follows becomes one `field_declaration` whose class key is an
11457/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
11458/// `initializer_list` (#3098).
11459struct CppCollapsedAggregateHead<'tree> {
11460    key: Node<'tree>,
11461    name: Node<'tree>,
11462    opening: Node<'tree>,
11463    /// The member list when the head folded it into its own node, which also
11464    /// carries the aggregate's closing brace. `None` leaves the aggregate open
11465    /// for the children that follow it.
11466    folded_members: Option<Node<'tree>>,
11467    /// How many children the head consumes.
11468    width: usize,
11469}
11470
11471/// The aggregate head that starts at `children[index]`, if one does.
11472fn cpp_collapsed_aggregate_head<'tree>(
11473    children: &[Node<'tree>],
11474    index: usize,
11475    source: &str,
11476) -> Option<CppCollapsedAggregateHead<'tree>> {
11477    let key = *children.get(index)?;
11478    if matches!(key.kind(), "struct" | "class" | "union") {
11479        let name = *children.get(index + 1)?;
11480        let opening = *children.get(index + 2)?;
11481        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11482            return None;
11483        }
11484        return Some(CppCollapsedAggregateHead {
11485            key,
11486            name,
11487            opening,
11488            folded_members: None,
11489            width: 3,
11490        });
11491    }
11492    cpp_folded_aggregate_head(key, source)
11493}
11494
11495/// The aggregate head tree-sitter folded into one declaration node, reading
11496/// the class key as an `ERROR` and the member list as an initializer.
11497fn cpp_folded_aggregate_head<'tree>(
11498    node: Node<'tree>,
11499    source: &str,
11500) -> Option<CppCollapsedAggregateHead<'tree>> {
11501    if !matches!(node.kind(), "declaration" | "field_declaration") {
11502        return None;
11503    }
11504    let mut cursor = node.walk();
11505    let children = node.children(&mut cursor).collect::<Vec<_>>();
11506    let key_index = children.iter().position(|child| {
11507        child.is_error()
11508            && matches!(
11509                node_text(*child, source).trim(),
11510                "struct" | "class" | "union"
11511            )
11512    })?;
11513    let declarator = *children.get(key_index + 1)?;
11514    // At file scope the grammar has an `init_declarator` for the name and its
11515    // initializer; inside an `ERROR` container the two are loose siblings.
11516    let (name, members) = if declarator.kind() == "init_declarator" {
11517        (
11518            declarator.child_by_field_name("declarator")?,
11519            declarator.child_by_field_name("value")?,
11520        )
11521    } else {
11522        (declarator, *children.get(key_index + 2)?)
11523    };
11524    if !matches!(
11525        name.kind(),
11526        "field_identifier" | "type_identifier" | "identifier"
11527    ) {
11528        return None;
11529    }
11530    if members.kind() != "initializer_list" {
11531        return None;
11532    }
11533    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11534    Some(CppCollapsedAggregateHead {
11535        key: children[key_index],
11536        name,
11537        opening,
11538        folded_members: Some(members),
11539        width: 1,
11540    })
11541}
11542
11543/// Where a folded member list ends: at its own closing brace when it has one,
11544/// so the reparse of the declarations it holds sees a member list and not a
11545/// stray brace.
11546fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11547    let mut cursor = members.walk();
11548    members
11549        .children(&mut cursor)
11550        .filter(|child| child.kind() == "}" && !child.is_missing())
11551        .last()
11552        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11553}
11554
11555/// The members one collapsed aggregate region declares, in source order: what
11556/// its field-list macro invocations donate, then the ordinary declarations
11557/// that follow the last invocation and lost their shape with the body.
11558fn cpp_collapsed_aggregate_members<'tree>(
11559    context: Node<'tree>,
11560    macro_nodes: &[Node<'tree>],
11561    region: std::ops::Range<usize>,
11562    region_start_line: usize,
11563    source: &str,
11564    environment: &HashMap<String, ObjectMacroReplacement>,
11565) -> Vec<CppCollapsedMember<'tree>> {
11566    let mut members = macro_nodes
11567        .iter()
11568        .map(|macro_node| CppCollapsedMember::MacroFields {
11569            context: *macro_node,
11570            range: cpp_declaration_range(*macro_node),
11571            fields: object_macro_field_closure(
11572                environment,
11573                &normalize_cpp_whitespace(node_text(*macro_node, source)),
11574            ),
11575        })
11576        .collect::<Vec<_>>();
11577    // Whatever follows the last invocation is an ordinary member list that
11578    // lost its declaration shape with the aggregate's body; the grammar
11579    // recovers it from its own byte range.
11580    let declarations_start = macro_nodes
11581        .last()
11582        .map_or(region.start, |macro_node| macro_node.end_byte());
11583    members.push(CppCollapsedMember::Declarations {
11584        context,
11585        span: declarations_start..region.end,
11586        start_line: region_start_line
11587            + cpp_line_breaks_between(source, region.start, declarations_start),
11588    });
11589    members
11590}
11591
11592/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
11593/// line without rescanning the file from its start.
11594fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11595    source.get(from..to).map_or(0, |slice| {
11596        slice.bytes().filter(|byte| *byte == b'\n').count()
11597    })
11598}
11599
11600/// How many enclosing aggregates a collapsed member declaration closes: the
11601/// close braces it carries that its own braces do not balance. A member that
11602/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
11603/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
11604/// owner early and lost every member after it (#2985).
11605fn count_close_brace_nodes(node: Node<'_>) -> usize {
11606    let mut opened = 0usize;
11607    let mut closed = 0usize;
11608    let mut stack = vec![node];
11609    while let Some(current) = stack.pop() {
11610        if !current.is_missing() {
11611            match current.kind() {
11612                "{" => opened += 1,
11613                "}" => closed += 1,
11614                _ => {}
11615            }
11616        }
11617        let mut cursor = current.walk();
11618        stack.extend(current.children(&mut cursor));
11619    }
11620    closed.saturating_sub(opened)
11621}
11622
11623/// Classify a C++ field while its declaration syntax is already available.
11624///
11625/// The persisted result lets later visibility queries avoid reparsing the
11626/// complete source file only to recover linkage.
11627pub fn cpp_field_declaration_linkage<'tree>(
11628    declaration: Node<'tree>,
11629    source: &str,
11630    ancestry: &ParentIndex<'tree>,
11631) -> CppFieldLinkage {
11632    let mut current = ancestry.parent(declaration);
11633    let mut enclosed_by_class = false;
11634    while let Some(node) = current {
11635        if node.kind() == "namespace_definition"
11636            && node
11637                .child_by_field_name("name")
11638                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11639        {
11640            return CppFieldLinkage::Internal;
11641        }
11642        if matches!(
11643            node.kind(),
11644            "class_specifier" | "struct_specifier" | "union_specifier"
11645        ) && node
11646            .child_by_field_name("name")
11647            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11648        {
11649            return CppFieldLinkage::Internal;
11650        }
11651        if matches!(
11652            node.kind(),
11653            "class_specifier" | "struct_specifier" | "union_specifier"
11654        ) {
11655            enclosed_by_class = true;
11656        }
11657        if matches!(node.kind(), "function_definition" | "lambda_expression") {
11658            return CppFieldLinkage::Internal;
11659        }
11660        current = ancestry.parent(node);
11661    }
11662    if enclosed_by_class {
11663        return CppFieldLinkage::External;
11664    }
11665    let mut cursor = declaration.walk();
11666    let mut has_static = false;
11667    let mut has_extern = false;
11668    let mut has_inline = false;
11669    let mut has_const = false;
11670    let mut has_constexpr = false;
11671    for child in declaration.named_children(&mut cursor) {
11672        let text = normalize_cpp_whitespace(node_text(child, source));
11673        match (child.kind(), text.as_str()) {
11674            ("storage_class_specifier", "static") => has_static = true,
11675            ("storage_class_specifier", "extern") => has_extern = true,
11676            ("storage_class_specifier", "inline") => has_inline = true,
11677            ("storage_class_specifier", "constexpr") => has_constexpr = true,
11678            ("type_qualifier", "const") => has_const = true,
11679            ("type_qualifier", "constexpr") => has_constexpr = true,
11680            _ => {}
11681        }
11682    }
11683    if has_static {
11684        CppFieldLinkage::Internal
11685    } else if has_extern || has_inline {
11686        CppFieldLinkage::External
11687    } else if has_const || has_constexpr {
11688        CppFieldLinkage::InternalUnlessExternalPeer
11689    } else {
11690        CppFieldLinkage::External
11691    }
11692}
11693
11694fn cpp_declaration_range(node: Node<'_>) -> Range {
11695    Range {
11696        start_byte: node.start_byte(),
11697        end_byte: node.end_byte(),
11698        start_line: node.start_position().row + 1,
11699        end_line: node.end_position().row + 1,
11700    }
11701}
11702
11703/// A recovery interval as a [`Range`], for materialization records whose
11704/// window is a byte region rather than one parser node (the sentinel-macro
11705/// region reparses, issue #941/#1657).
11706fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11707    let line_at = |byte: usize| {
11708        source.as_bytes()[..byte]
11709            .iter()
11710            .filter(|&&b| b == b'\n')
11711            .count()
11712            + 1
11713    };
11714    Range {
11715        start_byte,
11716        end_byte,
11717        start_line: line_at(start_byte),
11718        end_line: line_at(end_byte),
11719    }
11720}
11721
11722fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11723    ImportInfo {
11724        raw_snippet,
11725        is_wildcard: false,
11726        is_global: false,
11727        identifier: None,
11728        alias: None,
11729        path: None,
11730        binder_span: None,
11731    }
11732}
11733
11734fn recover_quoted_source_imports(
11735    source: &str,
11736    imports: &mut Vec<SourceImportFact>,
11737    includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11738    collector: &RefCell<PrimarySourceFactCollector<'_>>,
11739) {
11740    let mut in_block_comment = false;
11741    let mut start = 0;
11742    for line in source.split_inclusive('\n') {
11743        let end = start + line.len();
11744        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11745        let trimmed = stripped.trim();
11746        if looks_like_quoted_include_line(trimmed) {
11747            let raw = normalize_cpp_whitespace(trimmed);
11748            if !imports.iter().any(|import| import.statement == raw) {
11749                let occurrence = collector.borrow_mut().intern_subspan_bytes(
11750                    start,
11751                    end,
11752                    SourceOccurrenceProvenance::ExplicitSubspan,
11753                );
11754                // Recovery already identified a quoted directive hidden by a
11755                // malformed outer construct. Interpret its path with the same
11756                // grammar in an isolated included range, preserving byte identity.
11757                let region =
11758                    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11759                let target = region.as_ref().and_then(|tree| {
11760                    let mut pending = vec![tree.root_node()];
11761                    while let Some(node) = pending.pop() {
11762                        if node.kind() == "preproc_include"
11763                            && let Some(path) = node.child_by_field_name("path")
11764                            && path.kind() == "string_literal"
11765                        {
11766                            let (value, _) = crate::imports::include_path_from_node(node, source)?;
11767                            let target = collector.borrow_mut().intern_embedded(
11768                                brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11769                            );
11770                            includes.push(
11771                                brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11772                                    declaration: occurrence,
11773                                    target,
11774                                    path: value.to_owned(),
11775                                    quoted: true,
11776                                },
11777                            );
11778                            return Some(target);
11779                        }
11780                        let mut cursor = node.walk();
11781                        let children: Vec<_> = node.named_children(&mut cursor).collect();
11782                        pending.extend(children.into_iter().rev());
11783                    }
11784                    None
11785                });
11786                imports.push(SourceImportFact::from_import(
11787                    cpp_include_info(raw),
11788                    occurrence,
11789                    target,
11790                    None,
11791                    Vec::new(),
11792                ));
11793            }
11794        }
11795        start = end;
11796    }
11797}
11798
11799fn looks_like_quoted_include_line(line: &str) -> bool {
11800    let Some(rest) = line.trim_start().strip_prefix('#') else {
11801        return false;
11802    };
11803    let Some(rest) = rest.trim_start().strip_prefix("include") else {
11804        return false;
11805    };
11806    rest.trim_start().starts_with('"')
11807}
11808
11809fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11810    let mut raw = Vec::new();
11811    if let Some(bases) = cpp_class_like_base_clause(node) {
11812        collect_cpp_base_nodes(bases, source, &mut raw);
11813    }
11814    raw
11815}
11816
11817fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11818    let mut facts = Vec::new();
11819    let mut cursor = node.walk();
11820    for child in node.named_children(&mut cursor) {
11821        if child.kind() != "base_class_clause" {
11822            continue;
11823        }
11824        let mut is_virtual = false;
11825        let mut child_cursor = child.walk();
11826        for base in child.children(&mut child_cursor) {
11827            match base.kind() {
11828                "virtual" => is_virtual = true,
11829                "," => is_virtual = false,
11830                "type_identifier"
11831                | "qualified_identifier"
11832                | "scoped_type_identifier"
11833                | "template_type"
11834                | "template_function" => {
11835                    if let Some(fact) = crate::source_facts::base_specifier_from_node(
11836                        base,
11837                        source,
11838                        is_virtual,
11839                        crate::source_facts::base_access_specifier(child, base, node.kind()),
11840                    ) {
11841                        facts.push(fact);
11842                    }
11843                    is_virtual = false;
11844                }
11845                _ => {}
11846            }
11847        }
11848    }
11849    facts
11850}
11851
11852/// The `base_class_clause` a class-like node writes, if it writes one.
11853///
11854/// The pinned grammar exposes the clause as a named child rather than a field,
11855/// so the question is asked by kind. A base clause is the only place a C++
11856/// class body records that some other class may declare a member, which is
11857/// what [`cpp_callable_dispatch_extensibility`] reads it for.
11858fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11859    let mut cursor = class_like.walk();
11860    class_like
11861        .named_children(&mut cursor)
11862        .find(|child| child.kind() == "base_class_clause")
11863}
11864
11865fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11866    walk_named_tree_preorder(node, false, |child| match child.kind() {
11867        "type_identifier" | "qualified_identifier" | "template_type" => {
11868            let text = normalize_cpp_whitespace(node_text(child, source));
11869            if !text.is_empty() {
11870                raw.push(text);
11871            }
11872            WalkControl::SkipChildren
11873        }
11874        _ => WalkControl::Continue,
11875    });
11876}
11877
11878fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11879    let mut out = String::new();
11880    let chars: Vec<char> = line.chars().collect();
11881    let mut index = 0;
11882    let mut in_string = false;
11883    let mut in_char = false;
11884    let mut escape = false;
11885
11886    while index < chars.len() {
11887        let ch = chars[index];
11888        let next = chars.get(index + 1).copied();
11889
11890        if *in_block_comment {
11891            if ch == '*' && next == Some('/') {
11892                *in_block_comment = false;
11893                index += 2;
11894            } else {
11895                index += 1;
11896            }
11897            continue;
11898        }
11899
11900        if in_string {
11901            out.push(ch);
11902            if escape {
11903                escape = false;
11904            } else if ch == '\\' {
11905                escape = true;
11906            } else if ch == '"' {
11907                in_string = false;
11908            }
11909            index += 1;
11910            continue;
11911        }
11912
11913        if in_char {
11914            out.push(ch);
11915            if escape {
11916                escape = false;
11917            } else if ch == '\\' {
11918                escape = true;
11919            } else if ch == '\'' {
11920                in_char = false;
11921            }
11922            index += 1;
11923            continue;
11924        }
11925
11926        if ch == '/' && next == Some('/') {
11927            break;
11928        }
11929        if ch == '/' && next == Some('*') {
11930            *in_block_comment = true;
11931            index += 2;
11932            continue;
11933        }
11934        if ch == '"' {
11935            in_string = true;
11936            out.push(ch);
11937            index += 1;
11938            continue;
11939        }
11940        if ch == '\'' {
11941            in_char = true;
11942            out.push(ch);
11943            index += 1;
11944            continue;
11945        }
11946
11947        out.push(ch);
11948        index += 1;
11949    }
11950
11951    out
11952}
11953
11954#[derive(Clone)]
11955struct FunctionInfo {
11956    package_name: String,
11957    owner: Option<CppMemberOwner>,
11958    name: String,
11959    signature: String,
11960}
11961
11962/// Owner of a member function, kept structured so a literal `$` inside a
11963/// source-level class name never crosses a join/split boundary: the legacy
11964/// `$`-joined owner string was re-split at fq construction, dropping a leading
11965/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
11966/// and tripping the package/short boundary assert -- the #2140 corruption one
11967/// level up (#2362).
11968#[derive(Clone)]
11969enum CppMemberOwner {
11970    /// Source-level owner class chain from a qualified declarator-id, one
11971    /// class name per component (`Outer::Inner::method` -> `["Outer",
11972    /// "Inner"]`); each component may itself contain a literal `$`.
11973    Chain(Vec<String>),
11974    /// The lexically enclosing or recovered class unit; the member fq extends
11975    /// its fq directly instead of re-splitting its `$`-joined short chain.
11976    Unit(CodeUnit),
11977}
11978
11979impl CppMemberOwner {
11980    /// The legacy `$`-joined owner chain used in the member's short name.
11981    fn short_chain(&self) -> String {
11982        match self {
11983            Self::Chain(chain) => chain.join("$"),
11984            Self::Unit(parent) => parent.short_name().to_string(),
11985        }
11986    }
11987}
11988
11989enum DeclaratorKind<'a> {
11990    Function(Node<'a>),
11991    Variable(Node<'a>),
11992}
11993
11994impl FunctionInfo {
11995    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
11996        self.code_unit_with_synthetic(file, false)
11997    }
11998
11999    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
12000        let short_name = match &self.owner {
12001            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
12002            None => self.name.clone(),
12003        };
12004        let fq = match &self.owner {
12005            Some(CppMemberOwner::Chain(chain)) => {
12006                debug_assert!(
12007                    !chain.is_empty(),
12008                    "an empty owner chain is no owner; producers return None instead"
12009                );
12010                let mut fq = FqName::new();
12011                cpp_push_package(&mut fq, &self.package_name);
12012                let mut first = true;
12013                for component in chain {
12014                    let kind = if first {
12015                        SegmentKind::Type
12016                    } else {
12017                        SegmentKind::Nested
12018                    };
12019                    fq.push(segment_interner().intern(component, kind));
12020                    first = false;
12021                }
12022                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12023                fq
12024            }
12025            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
12026                .fq()
12027                .clone()
12028                .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
12029            // An anonymous parent (empty short chain) contributes no owner
12030            // segment -- the same guard as cpp_join_member_short above.
12031            Some(CppMemberOwner::Unit(_)) | None => {
12032                let mut fq = FqName::new();
12033                cpp_push_package(&mut fq, &self.package_name);
12034                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12035                fq
12036            }
12037        };
12038        CodeUnit::with_signature_and_fq(
12039            file,
12040            CodeUnitType::Function,
12041            self.package_name.clone(),
12042            short_name,
12043            Some(self.signature.clone()),
12044            synthetic,
12045            fq,
12046        )
12047    }
12048}
12049
12050fn extract_function_info(
12051    declarator: Node<'_>,
12052    source: &str,
12053    scope: &ScopeInfo,
12054) -> Option<FunctionInfo> {
12055    let parameters_node = declarator.child_by_field_name("parameters")?;
12056    let declarator_name_node = declarator
12057        .child_by_field_name("declarator")
12058        .or_else(|| parameters_node.prev_named_sibling())?;
12059    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
12060}
12061
12062fn extract_function_info_from_name(
12063    declarator: Node<'_>,
12064    declarator_name_node: Node<'_>,
12065    source: &str,
12066    scope: &ScopeInfo,
12067) -> Option<FunctionInfo> {
12068    let parameters_node = declarator.child_by_field_name("parameters")?;
12069    let parameters_text = cpp_parameter_signature(parameters_node, source);
12070    let recovered_specialization_member = scope
12071        .recovered_specialization_member_scope
12072        .then(|| {
12073            let terminal = declarator_name_node
12074                .child_by_field_name("name")
12075                .unwrap_or(declarator_name_node);
12076            let name = canonical_cpp_qualified_component(terminal, source)?.name;
12077            let owner = scope.class_unit.as_ref()?;
12078            Some((
12079                Some(CppMemberOwner::Unit(owner.clone())),
12080                name,
12081                scope.package_name.clone(),
12082            ))
12083        })
12084        .flatten();
12085    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
12086        parts
12087    } else if let Some(parts) =
12088        split_structured_templated_cpp_name(declarator, declarator_name_node, source, scope)
12089    {
12090        parts
12091    } else {
12092        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
12093            declarator_name_node,
12094            source,
12095        )?);
12096        if raw_name.is_empty() {
12097            return None;
12098        }
12099        let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
12100        match using_bound_cpp_owner(declarator_name_node, source, scope) {
12101            Some((owner_chain, package_name)) => {
12102                (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
12103            }
12104            None => (owner, name, package_name),
12105        }
12106    };
12107    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
12108    let mut signature = if suffix.is_empty() {
12109        parameters_text
12110    } else {
12111        format!("{parameters_text} {suffix}")
12112    };
12113    if let Some(template_signature) = &scope.template_signature {
12114        signature = format!("{template_signature}{signature}");
12115    }
12116
12117    Some(FunctionInfo {
12118        package_name,
12119        owner,
12120        name,
12121        signature,
12122    })
12123}
12124
12125/// Recover the semantic return type and callable name when a declaration macro
12126/// occupies a function definition's `type` field. Tree-sitter either exposes a
12127/// scalar return as the declarator's apparent name and the callable as the sole
12128/// identifier in an `ERROR`, or joins a template return and callable into a
12129/// qualified identifier with a missing `::`. Both shapes retain the complete
12130/// parameter list and body; a concrete separator remains an out-of-line member.
12131fn cpp_macro_displaced_callable_parts<'tree>(
12132    function_declarator: Node<'tree>,
12133    source: &str,
12134    ancestry: &ParentIndex<'tree>,
12135) -> Option<(Node<'tree>, Node<'tree>)> {
12136    let definition = ancestry.parent(function_declarator)?;
12137    if definition.kind() != "function_definition"
12138        || definition.child_by_field_name("declarator") != Some(function_declarator)
12139        || definition
12140            .child_by_field_name("body")
12141            .is_none_or(|body| body.kind() != "compound_statement")
12142    {
12143        return None;
12144    }
12145    let macro_type = definition.child_by_field_name("type")?;
12146    if macro_type.kind() != "type_identifier"
12147        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12148    {
12149        return None;
12150    }
12151
12152    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12153    if apparent_return_type.kind() == "qualified_identifier"
12154        && let (Some(return_type), Some(callable_name)) = (
12155            apparent_return_type.child_by_field_name("scope"),
12156            apparent_return_type.child_by_field_name("name"),
12157        )
12158        && return_type.kind() == "template_type"
12159        && matches!(callable_name.kind(), "identifier" | "field_identifier")
12160        && (0..apparent_return_type.child_count())
12161            .filter_map(|index| apparent_return_type.child(index))
12162            .any(|child| child.kind() == "::" && child.is_missing())
12163        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12164        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12165    {
12166        return Some((return_type, callable_name));
12167    }
12168    if !matches!(
12169        apparent_return_type.kind(),
12170        "identifier" | "field_identifier" | "type_identifier"
12171    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12172    {
12173        return None;
12174    }
12175    let parameters = function_declarator.child_by_field_name("parameters")?;
12176    let mut cursor = function_declarator.walk();
12177    let between = function_declarator
12178        .named_children(&mut cursor)
12179        .filter(|child| child.kind() != "comment")
12180        .filter(|child| {
12181            child.start_byte() >= apparent_return_type.end_byte()
12182                && child.end_byte() <= parameters.start_byte()
12183                && !same_node(*child, apparent_return_type)
12184                && !same_node(*child, parameters)
12185        })
12186        .collect::<Vec<_>>();
12187    let [name_error] = between.as_slice() else {
12188        return None;
12189    };
12190    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12191        return None;
12192    }
12193    let callable_name = name_error.named_child(0)?;
12194    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12195        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12196    {
12197        return None;
12198    }
12199    Some((apparent_return_type, callable_name))
12200}
12201
12202/// The part of a `function_declarator` after its parameter list that belongs to
12203/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
12204/// specification, a trailing return type and a trailing requires-clause.
12205///
12206/// The grammar makes each of these a distinct sibling of the `parameters`
12207/// field, so they are read from the tree. Splitting the declarator's text on
12208/// the parameter list instead silently dropped every qualifier whenever the
12209/// parameter list was spelled with whitespace that normalization rewrote - a
12210/// line break or a double space was enough to make a `const` member definition
12211/// a different logical symbol from its declaration (#1827).
12212///
12213/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
12214/// are deliberately excluded. C++ does not make them part of the signature and
12215/// an out-of-line definition never repeats them, so including them would split
12216/// a declaration from its own definition.
12217fn cpp_declarator_identity_suffix(
12218    declarator: Node<'_>,
12219    parameters_node: Node<'_>,
12220    source: &str,
12221) -> String {
12222    let mut cursor = declarator.walk();
12223    let parts = declarator
12224        .named_children(&mut cursor)
12225        .filter(|child| child.start_byte() >= parameters_node.end_byte())
12226        .filter(|child| {
12227            matches!(
12228                child.kind(),
12229                "type_qualifier"
12230                    | "ref_qualifier"
12231                    | "noexcept"
12232                    | "throw_specifier"
12233                    | "trailing_return_type"
12234                    | "requires_clause"
12235            )
12236        })
12237        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12238        .filter(|text| !text.is_empty())
12239        .collect::<Vec<_>>();
12240    normalize_cpp_qualifier_suffix(&parts.join(" "))
12241}
12242
12243/// The identity suffix of one callable declarator, for a consumer that holds
12244/// the declarator rather than the declaration walk's parts.
12245///
12246/// The persisted signature concatenates the parameter spelling and this suffix,
12247/// so a comparison that must agree on the suffix alone recomputes it here
12248/// instead of splitting the stored string.
12249pub(crate) fn cpp_callable_identity_suffix(
12250    function_declarator: Node<'_>,
12251    source: &str,
12252) -> Option<String> {
12253    let parameters_node = function_declarator.child_by_field_name("parameters")?;
12254    Some(cpp_declarator_identity_suffix(
12255        function_declarator,
12256        parameters_node,
12257        source,
12258    ))
12259}
12260
12261pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12262    match classify_declarator(node)? {
12263        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12264        DeclaratorKind::Variable(_) => None,
12265    }
12266}
12267
12268fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12269    match node.kind() {
12270        "function_declarator" => {
12271            let inner = node
12272                .child_by_field_name("declarator")
12273                .or_else(|| node.child_by_field_name("name"))
12274                .or_else(|| last_named_child(node));
12275            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12276                Some(DeclaratorKind::Variable(node))
12277            } else {
12278                Some(DeclaratorKind::Function(node))
12279            }
12280        }
12281        // A macro marker is declaration provenance, never a declarator: its
12282        // identifier names the macro, not anything the declaration declares.
12283        "macro_modifier" => None,
12284        "init_declarator"
12285        | "pointer_declarator"
12286        | "reference_declarator"
12287        | "parenthesized_declarator"
12288        | "array_declarator"
12289        | "attributed_declarator"
12290        | "template_function" => node
12291            .child_by_field_name("declarator")
12292            .or_else(|| node.child_by_field_name("name"))
12293            .or_else(|| last_named_child(node))
12294            .and_then(classify_declarator),
12295        "identifier" | "field_identifier" | "qualified_identifier" => {
12296            Some(DeclaratorKind::Variable(node))
12297        }
12298        _ => node
12299            .child_by_field_name("declarator")
12300            .or_else(|| node.child_by_field_name("name"))
12301            .or_else(|| last_named_child(node))
12302            .and_then(classify_declarator),
12303    }
12304}
12305
12306fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12307    matches!(
12308        node.kind(),
12309        "function_declarator"
12310            | "init_declarator"
12311            | "pointer_declarator"
12312            | "reference_declarator"
12313            | "parenthesized_declarator"
12314            | "array_declarator"
12315            | "attributed_declarator"
12316            | "template_function"
12317            | "macro_modifier_declarator"
12318            | "identifier"
12319            | "field_identifier"
12320            | "qualified_identifier"
12321    )
12322}
12323
12324fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12325    let class_like = first_class_like_child(node);
12326    let mut cursor = node.walk();
12327    node.named_children(&mut cursor).any(|child| {
12328        matches!(
12329            child.kind(),
12330            "init_declarator"
12331                | "pointer_declarator"
12332                | "reference_declarator"
12333                | "array_declarator"
12334                | "function_declarator"
12335                | "parenthesized_declarator"
12336                | "attributed_declarator"
12337        ) || matches!(
12338            child.kind(),
12339            "identifier" | "field_identifier" | "qualified_identifier"
12340        ) && class_like.is_none_or(|class_node| {
12341            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12342        })
12343    })
12344}
12345
12346/// One namespace-scope forward class declaration that a recovered export-macro
12347/// class definition may borrow its identity from.  Tree-sitter can close a
12348/// malformed class at the enclosing namespace's closing brace, leaving the later
12349/// class definitions as root-level recovered `function_definition` nodes.  A
12350/// preceding `class Name;` in the same namespace is the only structured identity
12351/// signal available in that shape.
12352///
12353/// Everything recorded here is a property of the forward declaration alone.
12354/// What depends on the node doing the asking -- that the forward and its
12355/// namespace both close before it, with nothing but recovery trivia between --
12356/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
12357/// tree answers every later question about it.
12358struct CppNamespaceForward {
12359    name: String,
12360    start_byte: usize,
12361    /// Where the malformed namespace that held the forward ended.  No query
12362    /// asks anything else about that node.
12363    namespace_end_byte: usize,
12364    package_name: String,
12365}
12366
12367/// Read `node` as a borrowable namespace forward declaration.
12368///
12369/// The admission is deliberately conservative: only a body-less class specifier
12370/// whose declaration has no declarator, at namespace scope rather than inside a
12371/// function or class body, in a namespace that itself failed to parse.
12372fn cpp_namespace_forward_entry<'tree>(
12373    node: Node<'tree>,
12374    source: &str,
12375    ancestry: &ParentIndex<'tree>,
12376) -> Option<CppNamespaceForward> {
12377    if !matches!(
12378        node.kind(),
12379        "class_specifier" | "struct_specifier" | "union_specifier"
12380    ) || cpp_body_node(node).is_some()
12381    {
12382        return None;
12383    }
12384    let parent = node.parent()?;
12385    if !(parent.kind() == "declaration_list"
12386        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12387    {
12388        return None;
12389    }
12390    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12391    // Borrowing is only justified by the parser-recovery shape we are
12392    // repairing: the namespace that held the forward must itself contain a
12393    // syntax error. A clean, unrelated namespace forward is not an identity
12394    // proof.
12395    if !namespace.has_error() {
12396        return None;
12397    }
12398    Some(CppNamespaceForward {
12399        name: class_like_name(node, source, ancestry)?,
12400        start_byte: node.start_byte(),
12401        namespace_end_byte: namespace.end_byte(),
12402        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12403    })
12404}
12405
12406/// Whether `forward` stands in the recovery relation to the node asking about
12407/// it: it and its malformed namespace both closed before the recovered class,
12408/// and nothing but recovery trivia separates the two.
12409fn cpp_namespace_forward_matches_recovery(
12410    forward: &CppNamespaceForward,
12411    recovered_node: Node<'_>,
12412) -> bool {
12413    forward.start_byte < recovered_node.start_byte()
12414        && forward.namespace_end_byte < recovered_node.start_byte()
12415        && malformed_namespace_is_nearest_recovery_region(
12416            forward.namespace_end_byte,
12417            recovered_node,
12418        )
12419}
12420
12421/// What one open [`CppVisitor::record_recovered_declarations`] has watched
12422/// happen to the declaration set.
12423///
12424/// The recovered set used to be a difference against a clone of the whole
12425/// declaration set, taken once per recovery: O(recoveries x declarations) on
12426/// exactly the error-recovered files that already walk slowest (#2787). The
12427/// walk knows which declarations it creates, so the capture collects them as
12428/// they are made and the difference is never needed.
12429///
12430/// `removed_pre_existing` is what makes that equal to the difference. A
12431/// deferred replacement removes the replaced declaration's children
12432/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
12433/// re-creates them. Creation alone cannot tell that apart from a first mint, so
12434/// a removal of a declaration this capture did not create records that it was
12435/// already there when the capture opened.
12436#[derive(Debug, Default)]
12437pub struct CppRecoveryCapture {
12438    /// Declarations created while this capture was open, in creation order.
12439    created: Vec<CodeUnit>,
12440    /// Membership for `created`.
12441    created_units: HashSet<CodeUnit>,
12442    /// Declarations that predate this capture and have been removed during it.
12443    removed_pre_existing: HashSet<CodeUnit>,
12444}
12445
12446/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
12447///
12448/// A region reparse is its own tree and is dropped while the walk that made it
12449/// continues, so a later parse can be allocated at the same address and hand out
12450/// the same node ids.  The root's span and shape pin the identity its address
12451/// alone does not: two roots agreeing on all of this are the same parse of the
12452/// same bytes, and a scan of one is a scan of the other.
12453#[derive(PartialEq, Eq, Hash)]
12454pub struct CppTreeIdentity {
12455    root_id: usize,
12456    start_byte: usize,
12457    end_byte: usize,
12458    kind_id: u16,
12459    child_count: usize,
12460}
12461
12462impl CppTreeIdentity {
12463    fn of(root: Node<'_>) -> Self {
12464        Self {
12465            root_id: root.id(),
12466            start_byte: root.start_byte(),
12467            end_byte: root.end_byte(),
12468            kind_id: root.kind_id(),
12469            child_count: root.child_count(),
12470        }
12471    }
12472}
12473
12474/// The namespace forward declarations one tree's prefix holds, folded in as the
12475/// walk asks about them.
12476///
12477/// `scope_for_recovered_exported_class` asks the same question once per
12478/// recovered class, and the answer depends only on the part of the tree that
12479/// starts before the asking node.  Rescanning that prefix per question is
12480/// quadratic in the file, and a generated header whose parse recovery leaves
12481/// thousands of class-like declarations at file scope pays all of it: 1,904
12482/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
12483/// (#2754).  This carries the scan forward instead.  Each question advances the
12484/// traversal from wherever the last one stopped to the asking node's start byte,
12485/// so a whole walk pays at most one pass over the prefix its furthest question
12486/// reaches, and each question then costs a name lookup.
12487#[derive(Default)]
12488pub struct CppNamespaceForwardScan {
12489    /// Every named node starting before this byte has been folded in.
12490    scanned_through: usize,
12491    forwards: HashMap<String, Vec<CppNamespaceForward>>,
12492}
12493
12494impl CppNamespaceForwardScan {
12495    /// Fold in every named node of `root` that starts at or after the fold
12496    /// watermark and before `cutoff`.
12497    ///
12498    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
12499    /// pass owes are exactly the ones no earlier pass reached, and a question
12500    /// about an earlier byte than one already answered costs nothing.
12501    fn advance_to<'tree>(
12502        &mut self,
12503        root: Node<'tree>,
12504        cutoff: usize,
12505        source: &str,
12506        ancestry: &ParentIndex<'tree>,
12507    ) {
12508        if cutoff <= self.scanned_through {
12509            return;
12510        }
12511        let folded_through = self.scanned_through;
12512        let mut cursor = root.walk();
12513        let mut stack = vec![root];
12514        while let Some(current) = stack.pop() {
12515            if (folded_through..cutoff).contains(&current.start_byte())
12516                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12517            {
12518                self.forwards
12519                    .entry(forward.name.clone())
12520                    .or_default()
12521                    .push(forward);
12522            }
12523            // A subtree ending before the watermark holds only nodes an earlier
12524            // pass already folded, and one starting at or after the cutoff is
12525            // outside the prefix being asked about. Skipping both is what keeps
12526            // the total traversal to one pass.
12527            for child in current.named_children(&mut cursor) {
12528                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12529                    stack.push(child);
12530                }
12531            }
12532        }
12533        self.scanned_through = cutoff;
12534    }
12535
12536    /// The one namespace `name` was forward declared in before `recovered_node`.
12537    /// More than one matching forward declaration is ambiguous and answers
12538    /// nothing rather than guessing.
12539    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12540        let mut matching = self
12541            .forwards
12542            .get(name)
12543            .into_iter()
12544            .flatten()
12545            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12546        let first = matching.next()?;
12547        matching
12548            .next()
12549            .is_none()
12550            .then(|| first.package_name.clone())
12551    }
12552}
12553
12554/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
12555/// debug build checks every answer against (and the one the parity tests drive
12556/// directly).  It walks the whole prefix per question, which is exactly the cost
12557/// #2754 removed from the release path.
12558#[cfg(any(debug_assertions, test))]
12559fn unique_earlier_cpp_namespace_forward<'tree>(
12560    recovered_node: Node<'tree>,
12561    name: &str,
12562    source: &str,
12563    ancestry: &ParentIndex<'tree>,
12564) -> Option<String> {
12565    let mut root = recovered_node;
12566    while let Some(parent) = ancestry.parent(root) {
12567        root = parent;
12568    }
12569
12570    let mut candidates = Vec::new();
12571    let mut stack = vec![root];
12572    while let Some(current) = stack.pop() {
12573        if current.start_byte() < recovered_node.start_byte()
12574            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12575            && forward.name == name
12576            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12577        {
12578            candidates.push(forward.package_name);
12579        }
12580
12581        let mut cursor = current.walk();
12582        for child in current.named_children(&mut cursor) {
12583            if child.start_byte() < recovered_node.start_byte() {
12584                stack.push(child);
12585            }
12586        }
12587    }
12588
12589    if candidates.len() == 1 {
12590        candidates.pop()
12591    } else {
12592        None
12593    }
12594}
12595
12596fn malformed_namespace_is_nearest_recovery_region(
12597    namespace_end_byte: usize,
12598    recovered_node: Node<'_>,
12599) -> bool {
12600    let mut root = recovered_node;
12601    while let Some(parent) = root.parent() {
12602        root = parent;
12603    }
12604    let mut cursor = root.walk();
12605    root.named_children(&mut cursor)
12606        .filter(|sibling| {
12607            namespace_end_byte <= sibling.start_byte()
12608                && sibling.end_byte() <= recovered_node.start_byte()
12609        })
12610        .all(is_malformed_namespace_recovery_trivia)
12611}
12612
12613fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12614    matches!(node.kind(), "ERROR" | "comment")
12615        || node.kind().starts_with("preproc_")
12616        || node.kind() == "expression_statement" && node.named_child_count() == 0
12617}
12618
12619/// Return the namespace path for a forward class only when the declaration is
12620/// at namespace scope.  A declaration nested in a function/class body may share
12621/// the same namespace ancestor but cannot identify a top-level class definition.
12622fn cpp_namespace_name_for_forward<'tree>(
12623    node: Node<'tree>,
12624    source: &str,
12625    ancestry: &ParentIndex<'tree>,
12626) -> Option<String> {
12627    cpp_namespace_definition_for_forward(node, ancestry)?;
12628    cpp_lexical_namespace_name(node, source)
12629}
12630
12631fn cpp_namespace_definition_for_forward<'tree>(
12632    node: Node<'tree>,
12633    ancestry: &ParentIndex<'tree>,
12634) -> Option<Node<'tree>> {
12635    let declaration = ancestry.parent(node)?;
12636    let mut ancestor = ancestry.parent(declaration);
12637    while let Some(current) = ancestor {
12638        if matches!(
12639            current.kind(),
12640            "compound_statement"
12641                | "field_declaration_list"
12642                | "class_specifier"
12643                | "struct_specifier"
12644                | "union_specifier"
12645                | "function_definition"
12646                | "lambda_expression"
12647        ) {
12648            return None;
12649        }
12650        if current.kind() == "namespace_definition" {
12651            return Some(current);
12652        }
12653        ancestor = ancestry.parent(current);
12654    }
12655    None
12656}
12657
12658fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12659    match node.kind() {
12660        "template_function" => node
12661            .child_by_field_name("name")
12662            .is_some_and(is_function_pointer_like_inner_declarator),
12663        _ => is_pointer_wrapper_declarator(node),
12664    }
12665}
12666
12667fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12668    match node.kind() {
12669        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12670        "parenthesized_declarator" => node
12671            .child_by_field_name("declarator")
12672            .or_else(|| last_named_child(node))
12673            .is_some_and(is_pointer_wrapper_declarator),
12674        _ => false,
12675    }
12676}
12677
12678/// The canonical owner chain and package of an out-of-line member whose
12679/// written qualifier starts with a name a `using ns::Type;` declaration bound
12680/// in the definition's own namespace scope (#6902 item 4).
12681///
12682/// `using imported::Type; void Type::method() {}` defines
12683/// `imported::Type::method`: the written `Type::` stays in the source, but the
12684/// member's identity follows the binding. The binding proves the owner only
12685/// when every visible binding of that name in this scope names the same
12686/// target. Conflicting bindings, a binding from an enclosing namespace (which
12687/// a same-named class in the inner namespace would shadow), and a using
12688/// declaration this walk cannot see (a preprocessor arm, another file) all
12689/// return `None`, and the caller keeps the written-qualifier reading.
12690fn using_bound_cpp_owner(
12691    declarator_name: Node<'_>,
12692    source: &str,
12693    scope: &ScopeInfo,
12694) -> Option<(Vec<String>, String)> {
12695    if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12696        return None;
12697    }
12698    let (components, explicitly_global) =
12699        structured_cpp_qualified_components(declarator_name, source)?;
12700    let (_, owner) = components.split_last()?;
12701    if explicitly_global
12702        || owner.is_empty()
12703        || components.iter().any(|component| component.is_template_id)
12704    {
12705        return None;
12706    }
12707    let mut targets = scope
12708        .visible_using_declarations
12709        .iter()
12710        .filter(|binding| {
12711            binding.scope_package == scope.package_name && binding.name == owner[0].name
12712        })
12713        .map(|binding| binding.target_package.as_str());
12714    let target_package = targets.next()?;
12715    if targets.any(|other| other != target_package) {
12716        return None;
12717    }
12718    Some((
12719        owner
12720            .iter()
12721            .map(|component| component.name.clone())
12722            .collect(),
12723        target_package.to_string(),
12724    ))
12725}
12726
12727fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12728    let cleaned = raw_name.trim_start_matches("template ").trim();
12729    // A leading `::` is the explicit-global marker, not an empty owner segment.
12730    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
12731    // erroneous macro envelope swallowing the first identifier of an
12732    // out-of-line `X::X` constructor, chromium #1573); without this strip the
12733    // split below yields owner_parts `[""]`, constructing a unit with an empty
12734    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
12735    let cleaned = cleaned.trim_start_matches("::");
12736    // Parser recovery can preserve two adjacent scope operators around a
12737    // missing component (for example `X::/**/::method` in compiler diagnostic
12738    // fixtures). Empty components are syntax-recovery artifacts, never C++
12739    // owners. Keeping one as the final owner constructed `short_name=".method"`
12740    // and violated the structured package/short boundary during a large LLVM
12741    // workspace build. This is the same legacy-string-to-FqName bridge as the
12742    // ordinary split above; discard only components that the delimiter itself
12743    // proves empty.
12744    let parts: Vec<_> = cleaned
12745        .split("::")
12746        .filter(|component| !component.is_empty())
12747        .collect();
12748    if parts.is_empty() {
12749        return (None, cleaned.to_string(), scope.package_name.clone());
12750    }
12751    if parts.len() > 1 {
12752        let name = parts.last().unwrap_or(&cleaned).to_string();
12753        let owner_parts = &parts[..parts.len() - 1];
12754        if let Some(class_unit) = &scope.class_unit {
12755            // Lexically inside a class body: the owner is that class, whatever
12756            // the declarator re-qualifies it as.
12757            return (
12758                Some(CppMemberOwner::Unit(class_unit.clone())),
12759                name,
12760                scope.package_name.clone(),
12761            );
12762        }
12763        if !scope.package_name.is_empty() {
12764            // Out-of-line member definition written *inside* an enclosing
12765            // `namespace {}` block (scope package is that namespace). Every
12766            // owner segment before the terminal member is a class-nesting step
12767            // -- an out-of-line nested-class member `Outer::Inner::method` in
12768            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
12769            // namespace path: `using namespace` never brings nested-class
12770            // access into unqualified scope, so C++ always writes the full
12771            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
12772            // that redundantly re-states the enclosing namespace it already
12773            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
12774            // strip that re-qualifying prefix (which duplicates a suffix of the
12775            // enclosing package path) before treating what remains as the
12776            // nested-class chain, so the redundant spelling still lands on the
12777            // same `log4cxx.Foo.method` identity as its header declaration.
12778            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12779            let owner = (!nested.is_empty()).then(|| {
12780                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12781            });
12782            return (owner, name, scope.package_name.clone());
12783        }
12784        // File scope (no enclosing `namespace {}` block, scope package empty).
12785        let (owner, package_name) = if owner_parts.len() > 1 {
12786            // A multi-segment qualifier at file scope with no enclosing
12787            // namespace: treat all but the last owner segment as the namespace
12788            // path and the last as the owning class (`ns1::ns2::Class::method`
12789            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
12790            // is really a namespace or an outer class cannot be told from the
12791            // declarator text alone here, and no enclosing namespace or
12792            // in-index owner is available at per-file extraction to confirm the
12793            // class reading, so the far-more-common namespace interpretation is
12794            // kept rather than guessed away (the nested-class-at-file-scope and
12795            // using-directive-qualified nested-class shapes remain on this
12796            // behavior; see #1121).
12797            (
12798                Some(CppMemberOwner::Chain(vec![
12799                    owner_parts.last().unwrap_or(&"").to_string(),
12800                ])),
12801                owner_parts[..owner_parts.len() - 1].join("::"),
12802            )
12803        } else {
12804            // A bare `Class::member` qualifier at file scope carries no
12805            // namespace segment of its own. The declarator alone cannot say
12806            // which namespace owns `Class` -- but a `using namespace X;`
12807            // directive already in effect at this point in the file (#1093,
12808            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
12809            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
12810            // is the remaining structural signal for it, so fall back to it
12811            // rather than leaving the definition's package empty while its
12812            // header declaration (parsed inside the `namespace {}` block) keeps
12813            // the real one -- an identity split that made the same member
12814            // unresolvable under its own displayed spelling.
12815            (
12816                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12817                cpp_using_directive_namespace_for_bare_owner(scope),
12818            )
12819        };
12820        return (owner, name, package_name);
12821    }
12822
12823    let package_name = scope.package_name.clone();
12824    let owner = scope
12825        .class_unit
12826        .as_ref()
12827        .map(|parent| CppMemberOwner::Unit(parent.clone()));
12828    (owner, cleaned.to_string(), package_name)
12829}
12830
12831/// Drop the leading owner segments of an out-of-line member qualifier that
12832/// merely re-state the enclosing namespace the definition already sits in, so
12833/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
12834/// definition may redundantly write `a::b::Outer::Inner::method` (or the
12835/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
12836/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
12837/// noise, not class-nesting steps. Returns the owner segments with the longest
12838/// such re-qualifying prefix removed (possibly all of them, when the qualifier
12839/// names only the enclosing namespace before the terminal member -- a
12840/// re-qualified free function). `package_name` is the enclosing namespace path
12841/// in its stored `::`-joined form; both sides are split on the same delimiter
12842/// the namespace walker joined them with, so this compares namespace *segments*
12843/// rather than scanning text.
12844fn strip_redundant_namespace_prefix<'a>(
12845    owner_parts: &'a [&'a str],
12846    package_name: &str,
12847) -> &'a [&'a str] {
12848    if package_name.is_empty() {
12849        return owner_parts;
12850    }
12851    let package_segments: Vec<&str> = package_name.split("::").collect();
12852    let max_prefix = owner_parts.len().min(package_segments.len());
12853    for prefix_len in (1..=max_prefix).rev() {
12854        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12855        if &owner_parts[..prefix_len] == package_suffix {
12856            return &owner_parts[prefix_len..];
12857        }
12858    }
12859    owner_parts
12860}
12861
12862/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
12863/// class name at file/namespace scope, from the `using namespace` directives
12864/// visible at this point in the file. Several may be in scope at once (a
12865/// primary `using namespace NS;` alongside deeper conveniences like `using
12866/// namespace NS::helpers;`); since the declarator gives no way to tell which
12867/// one actually declares the owner class, prefer the shallowest (fewest
12868/// `::`-separated segments) as the file's most likely "home" namespace,
12869/// breaking ties by declaration order. Returns an empty string (leaving the
12870/// caller's package unqualified, as before) when no using-namespace directive
12871/// is in scope.
12872fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12873    scope
12874        .visible_using_namespaces
12875        .iter()
12876        .min_by_key(|namespace| namespace.split("::").count())
12877        .cloned()
12878        .unwrap_or_default()
12879}
12880
12881struct CppQualifiedNameComponent {
12882    name: String,
12883    is_template_id: bool,
12884}
12885
12886/// Canonical nested-class chain for an out-of-line class definition written
12887/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
12888/// component per class (`["Outer", "Inner"]`).
12889///
12890/// The enclosing namespace fixes the namespace/class boundary: after an
12891/// optional redundant spelling of that namespace, every component belongs to
12892/// the class chain. File-scope qualified class names remain untouched because
12893/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
12894///
12895/// The components stay structured (rather than being `$`-joined here) so the
12896/// fq construction can push one Type/Nested segment per class; the `$`-joined
12897/// short-name display form is derived at the call sites that need it.
12898fn qualified_class_name_chain(
12899    class_node: Node<'_>,
12900    source: &str,
12901    scope: &ScopeInfo,
12902) -> Option<Vec<String>> {
12903    if scope.package_name.is_empty() || scope.class_unit.is_some() {
12904        return None;
12905    }
12906    let name = class_node.child_by_field_name("name")?;
12907    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12908    if explicitly_global
12909        || components.len() < 2
12910        || components.iter().any(|component| component.is_template_id)
12911    {
12912        return None;
12913    }
12914    let names = components
12915        .iter()
12916        .map(|component| component.name.as_str())
12917        .collect::<Vec<_>>();
12918    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12919    if class_chain.is_empty() {
12920        return None;
12921    }
12922    Some(class_chain.iter().map(|name| name.to_string()).collect())
12923}
12924
12925fn structured_cpp_qualified_components(
12926    qualified_name: Node<'_>,
12927    source: &str,
12928) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12929    if qualified_name.kind() != "qualified_identifier" {
12930        return None;
12931    }
12932
12933    let mut components = Vec::new();
12934    let mut current = qualified_name;
12935    let mut explicitly_global = false;
12936    loop {
12937        if current.kind() == "qualified_identifier" {
12938            if let Some(component) = current.child_by_field_name("scope") {
12939                components.push(canonical_cpp_qualified_component(component, source)?);
12940            } else if components.is_empty() {
12941                explicitly_global = true;
12942            } else {
12943                return None;
12944            }
12945            current = current.child_by_field_name("name")?;
12946        } else {
12947            components.push(canonical_cpp_qualified_component(current, source)?);
12948            break;
12949        }
12950    }
12951    Some((components, explicitly_global))
12952}
12953
12954fn split_structured_templated_cpp_name(
12955    declarator: Node<'_>,
12956    declarator_name: Node<'_>,
12957    source: &str,
12958    scope: &ScopeInfo,
12959) -> Option<(Option<CppMemberOwner>, String, String)> {
12960    let (mut components, explicitly_global) =
12961        structured_cpp_qualified_components(declarator_name, source)?;
12962
12963    let terminal = components.pop()?;
12964    let owner_start = components
12965        .iter()
12966        .position(|component| component.is_template_id)?;
12967    let explicit_package = components[..owner_start]
12968        .iter()
12969        .map(|component| component.name.as_str())
12970        .collect::<Vec<_>>()
12971        .join("::");
12972    let explicit_package_is_empty = explicit_package.is_empty();
12973    let package_name = match (
12974        explicitly_global,
12975        scope.package_name.is_empty(),
12976        explicit_package_is_empty,
12977    ) {
12978        (true, _, _) => explicit_package,
12979        (false, _, true) => scope.package_name.clone(),
12980        (false, true, false) => explicit_package,
12981        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12982    };
12983    // Same identity-split fallback as `split_cpp_name` (#1093): a template
12984    // specialization's owner class named with no namespace segment of its own
12985    // (`explicit_package` empty) at file scope (`explicitly_global` false)
12986    // with nothing enclosing (`package_name` still empty) has no structural
12987    // signal for its namespace besides an in-scope `using namespace X;`.
12988    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12989    {
12990        cpp_using_directive_namespace_for_bare_owner(scope)
12991    } else {
12992        package_name
12993    };
12994    // A specialization member keeps its specialization owner (`Box<T*>`)
12995    // rather than collapsing into the primary's; a qualifier the template
12996    // levels cannot read keeps the bare names.
12997    let owner_chain = match cpp_out_of_line_owner_names(declarator, source)
12998        .filter(|names| names.len() == components.len())
12999    {
13000        Some(mut names) => names.split_off(owner_start),
13001        None => components[owner_start..]
13002            .iter()
13003            .map(|component| component.name.clone())
13004            .collect::<Vec<_>>(),
13005    };
13006    if owner_chain.is_empty() || terminal.name.is_empty() {
13007        return None;
13008    }
13009
13010    Some((
13011        Some(CppMemberOwner::Chain(owner_chain)),
13012        terminal.name,
13013        package_name,
13014    ))
13015}
13016
13017fn canonical_cpp_qualified_component(
13018    mut component: Node<'_>,
13019    source: &str,
13020) -> Option<CppQualifiedNameComponent> {
13021    let mut is_template_id = false;
13022    loop {
13023        match component.kind() {
13024            "template_type" => {
13025                is_template_id = true;
13026                component = component.child_by_field_name("name")?;
13027            }
13028            "dependent_name" => component = component.named_child(0)?,
13029            "identifier"
13030            | "field_identifier"
13031            | "namespace_identifier"
13032            | "type_identifier"
13033            | "operator_name"
13034            | "destructor_name" => {
13035                let name = normalize_cpp_whitespace(node_text(component, source));
13036                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
13037                    name,
13038                    is_template_id,
13039                });
13040            }
13041            _ => component = component.child_by_field_name("name")?,
13042        }
13043    }
13044}
13045
13046fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
13047    if let Some(name) = macro_decorated_unqualified_name(node) {
13048        return extract_declarator_name(name, source);
13049    }
13050    match node.kind() {
13051        "identifier"
13052        | "field_identifier"
13053        | "type_identifier"
13054        | "operator_name"
13055        | "destructor_name"
13056        | "qualified_identifier" => node_text(node, source).to_string(),
13057        "function_declarator"
13058        | "pointer_declarator"
13059        | "reference_declarator"
13060        | "parenthesized_declarator"
13061        | "array_declarator"
13062        | "template_function" => node
13063            .child_by_field_name("declarator")
13064            .or_else(|| node.child_by_field_name("name"))
13065            .or_else(|| last_named_child(node))
13066            .map(|child| extract_declarator_name(child, source))
13067            .unwrap_or_else(|| node_text(node, source).to_string()),
13068        _ => node
13069            .child_by_field_name("name")
13070            .map(|child| extract_declarator_name(child, source))
13071            .unwrap_or_else(|| node_text(node, source).to_string()),
13072    }
13073}
13074
13075/// Extract a callable identity only through declaration-shaped AST nodes.
13076/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
13077/// expose the call's parameter list as a false function declarator; accepting
13078/// arbitrary node text there emitted bogus names such as `.*f`.
13079fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13080    if let Some(name) = macro_decorated_unqualified_name(node) {
13081        return extract_callable_declarator_name(name, source);
13082    }
13083    match node.kind() {
13084        "identifier"
13085        | "field_identifier"
13086        | "type_identifier"
13087        | "operator_name"
13088        | "destructor_name"
13089        | "qualified_identifier" => Some(node_text(node, source).to_string()),
13090        "function_declarator"
13091        | "pointer_declarator"
13092        | "reference_declarator"
13093        | "parenthesized_declarator"
13094        | "array_declarator"
13095        | "template_function" => node
13096            .child_by_field_name("declarator")
13097            .or_else(|| node.child_by_field_name("name"))
13098            .and_then(|child| extract_callable_declarator_name(child, source)),
13099        _ => None,
13100    }
13101}
13102
13103fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
13104    loop {
13105        if let Some(name) = macro_decorated_unqualified_name(node) {
13106            node = name;
13107            continue;
13108        }
13109        match node.kind() {
13110            "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
13111                let name = node_text(node, source).trim().to_string();
13112                return (!name.is_empty()).then_some(name);
13113            }
13114            _ => {
13115                node = node
13116                    .child_by_field_name("declarator")
13117                    .or_else(|| node.child_by_field_name("name"))
13118                    .or_else(|| last_named_child(node))?;
13119            }
13120        }
13121    }
13122}
13123
13124/// Recover a C field whose name is wrapped in the function-like
13125/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
13126/// function declarator, while retaining its argument as a structured child.
13127/// The exact macro name and one-argument shape keep ordinary function-pointer
13128/// fields and unknown malformed declarators fail-closed.
13129#[derive(Clone, Copy)]
13130pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13131    pub(crate) name: Node<'tree>,
13132    pub(crate) declarator: Node<'tree>,
13133}
13134
13135impl RecoveredFunctionLikeFieldDeclarator<'_> {
13136    pub(crate) fn pointer_depth(self) -> i32 {
13137        let mut depth = 0;
13138        let mut current = self.declarator;
13139        while current.kind() != "function_declarator" {
13140            if current.kind() == "pointer_declarator" {
13141                depth += 1;
13142            }
13143            current = current
13144                .child_by_field_name("declarator")
13145                .expect("recovered field wrapper has an inner declarator");
13146        }
13147        depth
13148    }
13149}
13150
13151/// Whether a class-scope field declaration is really a function-like macro
13152/// invocation whose replacement the parser never saw.
13153///
13154/// The invocation carries no terminator of its own, so tree-sitter invents a
13155/// zero-width `;` and reads the macro name as the member's type and the
13156/// argument list as a `parenthesized_declarator`. That shape is not a member:
13157/// a member terminates itself, and with a real terminator the grammar reads
13158/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
13159/// type can name the macro being invoked; a builtin type keeps whatever
13160/// recovery the ordinary declarator walk makes of it.
13161fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13162    if node.kind() != "field_declaration" {
13163        return false;
13164    }
13165    let Some(type_node) = node.child_by_field_name("type") else {
13166        return false;
13167    };
13168    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13169        return false;
13170    }
13171    let Some(declarator) = node.child_by_field_name("declarator") else {
13172        return false;
13173    };
13174    if declarator.kind() != "parenthesized_declarator" {
13175        return false;
13176    }
13177    let mut invented_terminator = false;
13178    for child in children_iter(node) {
13179        if child.kind() != ";" {
13180            continue;
13181        }
13182        if !child.is_missing() {
13183            return false;
13184        }
13185        invented_terminator = true;
13186    }
13187    invented_terminator
13188}
13189
13190/// An access label that a class-scope function-like macro invocation
13191/// swallowed, as [`cpp_macro_swallowed_access_label`] reads it.
13192pub(crate) struct CppSwallowedAccessLabel<'tree> {
13193    /// The access keyword, which the grammar read as a `field_identifier`.
13194    pub(crate) keyword: Node<'tree>,
13195    /// `"public"`, `"protected"` or `"private"`.
13196    pub(crate) access: &'static str,
13197}
13198
13199/// Whether a class-body member is a function-like macro invocation that
13200/// swallowed the access label written after it, and with it the members under
13201/// that label.
13202///
13203/// `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)` carries no terminator of its own
13204/// (#3298). The macro-declaration grammar (brokk-tree-sitter-cpp 0.24.4) reads
13205/// the invocation as a `macro_type_specifier`, the `public` after it as the
13206/// member's `field_identifier` declarator, and the label's `:` as a bitfield
13207/// whose width expression shreds the next member, so the constructor written
13208/// under the label is left with no declarator at all. An access keyword is
13209/// never a declarator name, and a bitfield after it is how the parser spent
13210/// the label's colon, so the shape is the evidence. The access ladders read
13211/// the label from it too, because it still applies to every member after it.
13212pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13213    node: Node<'tree>,
13214    source: &str,
13215) -> Option<CppSwallowedAccessLabel<'tree>> {
13216    if !matches!(node.kind(), "field_declaration" | "function_definition") {
13217        return None;
13218    }
13219    node.child_by_field_name("type")
13220        .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13221    let keyword = node
13222        .child_by_field_name("declarator")
13223        .filter(|declarator| declarator.kind() == "field_identifier")?;
13224    let access = match node_text(keyword, source) {
13225        "public" => "public",
13226        "protected" => "protected",
13227        "private" => "private",
13228        _ => return None,
13229    };
13230    let mut clause = keyword.next_sibling()?;
13231    if clause.kind() == "ERROR" {
13232        clause = clause.child(0)?;
13233    }
13234    if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13235        return None;
13236    }
13237    Some(CppSwallowedAccessLabel { keyword, access })
13238}
13239
13240pub(crate) fn recovered_function_like_field_declarator<'tree>(
13241    node: Node<'tree>,
13242    source: &str,
13243) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13244    if node.kind() != "field_declaration" {
13245        return None;
13246    }
13247    let outer_declarator = node.child_by_field_name("declarator")?;
13248    let mut declarator = outer_declarator;
13249    while matches!(
13250        declarator.kind(),
13251        "pointer_declarator"
13252            | "reference_declarator"
13253            | "array_declarator"
13254            | "parenthesized_declarator"
13255    ) {
13256        declarator = declarator.child_by_field_name("declarator")?;
13257    }
13258    if declarator.kind() != "function_declarator" {
13259        return None;
13260    }
13261    let macro_name = declarator.child_by_field_name("declarator")?;
13262    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13263    {
13264        return None;
13265    }
13266    let parameters = declarator.child_by_field_name("parameters")?;
13267    let mut cursor = parameters.walk();
13268    let mut arguments = parameters.named_children(&mut cursor);
13269    let parameter = arguments.next()?;
13270    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13271        return None;
13272    }
13273    let name = parameter.child_by_field_name("type").filter(|argument| {
13274        matches!(
13275            argument.kind(),
13276            "identifier" | "field_identifier" | "type_identifier"
13277        )
13278    })?;
13279    Some(RecoveredFunctionLikeFieldDeclarator {
13280        name,
13281        declarator: outer_declarator,
13282    })
13283}
13284
13285use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13286
13287fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13288    let name_node = node.child_by_field_name("name")?;
13289    let name = normalize_cpp_whitespace(node_text(name_node, source));
13290    (!name.is_empty()).then_some(name)
13291}
13292
13293fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13294    if node.kind() != "declaration" {
13295        return Vec::new();
13296    }
13297    // The `typedef`/`using` keyword: a `type_identifier` in the type field,
13298    // or -- under the macro-declaration grammar, which reads the keyword as
13299    // an object-like macro -- the identifier a leading `macro_modifier`
13300    // wraps, with the aliased type in the type field.
13301    let keyword_is_alias = node
13302        .child_by_field_name("type")
13303        .filter(|node| {
13304            node.kind() == "type_identifier"
13305                && matches!(node_text(*node, source), "using" | "typedef")
13306        })
13307        .map(|keyword| node_text(keyword, source).to_owned())
13308        .or_else(|| {
13309            node.named_children(&mut node.walk())
13310                .find_map(macro_modifier_identifier)
13311                .map(|identifier| node_text(identifier, source).to_owned())
13312                .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13313        });
13314    let Some(keyword_is_alias) = keyword_is_alias else {
13315        return Vec::new();
13316    };
13317    let Some(declarator) = node.child_by_field_name("declarator") else {
13318        return Vec::new();
13319    };
13320    if keyword_is_alias == "using"
13321        && (declarator.kind() != "init_declarator"
13322            || declarator.child_by_field_name("value").is_none())
13323    {
13324        return Vec::new();
13325    }
13326    if keyword_is_alias == "typedef"
13327        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13328    {
13329        return vec![alias_name];
13330    }
13331    extract_typedef_declarator_name(declarator, source)
13332        .into_iter()
13333        .collect()
13334}
13335
13336/// The RHS retained by the same malformed alias shapes declaration admission
13337/// accepts. The alias name proves which declarator owns this source evidence.
13338pub(crate) fn recovered_alias_type_node<'tree>(
13339    node: Node<'tree>,
13340    alias_name: &str,
13341    source: &str,
13342) -> Option<Node<'tree>> {
13343    if node.kind() != "declaration" {
13344        return None;
13345    }
13346    let declarator = node.child_by_field_name("declarator")?;
13347    let admitted = recovered_type_alias_names(node, source)
13348        .iter()
13349        .any(|name| name == alias_name)
13350        || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13351    if !admitted {
13352        return None;
13353    }
13354    if declarator.kind() == "init_declarator" {
13355        return declarator.child_by_field_name("value");
13356    }
13357    // The macro-declaration grammar keeps the aliased type in the type field
13358    // and the keyword as a `macro_modifier`; the alias name is the
13359    // declarator, so the type field is the RHS evidence.
13360    if node
13361        .named_children(&mut node.walk())
13362        .find_map(macro_modifier_identifier)
13363        .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13364    {
13365        return node.child_by_field_name("type");
13366    }
13367    (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13368        .then_some(declarator)
13369}
13370
13371fn recovered_typedef_error_alias_name(
13372    declaration: Node<'_>,
13373    declarator: Node<'_>,
13374    source: &str,
13375) -> Option<String> {
13376    // An export macro between `class` and its name can make tree-sitter parse
13377    // the recovered class body as a function body. In that shape,
13378    //
13379    //     typedef spi::Filter BASE_CLASS;
13380    //
13381    // becomes a declaration whose `declarator` is the underlying qualified
13382    // type (`spi::Filter`) and whose actual alias name is displaced into the
13383    // following ERROR node. Do not publish the terminal underlying type
13384    // (`Filter`) as a false class-owned alias.
13385    if declarator.kind() != "qualified_identifier" {
13386        return None;
13387    }
13388    let mut cursor = declaration.walk();
13389    let mut errors = declaration
13390        .named_children(&mut cursor)
13391        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13392    let error = errors.next()?;
13393    if errors.next().is_some() || error.named_child_count() != 1 {
13394        return None;
13395    }
13396    let name = error.named_child(0)?;
13397    if !matches!(
13398        name.kind(),
13399        "identifier" | "field_identifier" | "type_identifier"
13400    ) {
13401        return None;
13402    }
13403    let name = normalize_cpp_whitespace(node_text(name, source));
13404    (!name.is_empty()).then_some(name)
13405}
13406
13407fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13408    // A function-like token in the type position can make tree-sitter expose
13409    // its argument as a parenthesized declarator. Do not publish that argument
13410    // as an alias. The macro-specific recovery below handles the proven shape.
13411    if fragmented_parenthesized_typedef_type(node).is_some() {
13412        return Vec::new();
13413    }
13414    let has_function_like_macro_type = node
13415        .child_by_field_name("type")
13416        .filter(|type_node| type_node.kind() == "type_identifier")
13417        .is_some_and(|type_node| {
13418            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13419        });
13420    let mut names = Vec::new();
13421    let mut cursor = node.walk();
13422    for declarator in node.children_by_field_name("declarator", &mut cursor) {
13423        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13424            continue;
13425        }
13426        if let Some(name) = extract_typedef_declarator_name(declarator, source)
13427            && !names.contains(&name)
13428        {
13429            names.push(name);
13430        }
13431    }
13432    names
13433}
13434
13435struct RecoveredMacroTypedefAlias<'tree> {
13436    name: String,
13437    end_node: Node<'tree>,
13438}
13439
13440/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
13441/// into an identifier expression statement. The uppercase macro token, missing
13442/// typedef terminator, and complete sibling terminator prove this exact shape.
13443fn recovered_macro_typedef_alias<'tree>(
13444    node: Node<'tree>,
13445    source: &str,
13446) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13447    let type_node = fragmented_parenthesized_typedef_type(node)?;
13448    if type_node.kind() != "type_identifier"
13449        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13450    {
13451        return None;
13452    }
13453
13454    let end_node = node.next_named_sibling()?;
13455    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13456        return None;
13457    }
13458    let name_node = end_node.named_child(0)?;
13459    if name_node.kind() != "identifier" {
13460        return None;
13461    }
13462    let has_terminator = (0..end_node.child_count()).any(|index| {
13463        end_node
13464            .child(index)
13465            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13466    });
13467    if !has_terminator {
13468        return None;
13469    }
13470    let name = normalize_cpp_whitespace(node_text(name_node, source));
13471    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13472}
13473
13474fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13475    if node.kind() != "type_definition" {
13476        return None;
13477    }
13478    let mut declarator_cursor = node.walk();
13479    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13480    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13481        return None;
13482    }
13483    let has_missing_terminator = (0..node.child_count()).any(|index| {
13484        node.child(index)
13485            .is_some_and(|child| child.kind() == ";" && child.is_missing())
13486    });
13487    if !has_missing_terminator {
13488        return None;
13489    }
13490    node.child_by_field_name("type")
13491}
13492
13493pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13494    let mut stack = vec![node];
13495    while let Some(node) = stack.pop() {
13496        let child = match node.kind() {
13497            "identifier" | "field_identifier" | "type_identifier" => {
13498                return direct_identifier_name(node, source);
13499            }
13500            "qualified_identifier" => node.child_by_field_name("name")?,
13501            _ => node
13502                .child_by_field_name("declarator")
13503                .or_else(|| node.child_by_field_name("name"))
13504                .or_else(|| last_named_child(node))?,
13505        };
13506        stack.push(child);
13507    }
13508    None
13509}
13510
13511fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13512    if let Some(name_node) = node.child_by_field_name("name") {
13513        let name = normalize_cpp_whitespace(node_text(name_node, source));
13514        return (!name.is_empty()).then_some(name);
13515    }
13516    let mut cursor = node.walk();
13517    node.named_children(&mut cursor)
13518        .find(|child| {
13519            matches!(
13520                child.kind(),
13521                "identifier" | "field_identifier" | "type_identifier"
13522            )
13523        })
13524        .and_then(|name_node| direct_identifier_name(name_node, source))
13525}
13526
13527fn render_cpp_type_signature(
13528    node: Node<'_>,
13529    source: &str,
13530    template_signature: Option<&str>,
13531) -> String {
13532    let text = normalize_cpp_whitespace(node_text(node, source));
13533    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13534    let rendered = if head.ends_with(';') {
13535        head.to_string()
13536    } else {
13537        format!("{head} {{")
13538    };
13539    if let Some(template_signature) = template_signature {
13540        format!("template {template_signature} {rendered}")
13541    } else {
13542        rendered
13543    }
13544}
13545
13546fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13547    if let Some(recovered) = recovered_pyobject_head_field(node, source)
13548        && recovered.declarator == declarator
13549    {
13550        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13551        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13552        return format!("{type_text} {declarator};");
13553    }
13554    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13555        && recovered.name == declarator
13556    {
13557        let type_text = node
13558            .child_by_field_name("type")
13559            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13560            .unwrap_or_default();
13561        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13562        let mut prefix = String::new();
13563        let mut suffix = String::new();
13564        let mut current = recovered.declarator;
13565        while current.kind() != "function_declarator" {
13566            match current.kind() {
13567                "pointer_declarator" => prefix.push('*'),
13568                "reference_declarator" => prefix.push('&'),
13569                "array_declarator" => {
13570                    let size = current
13571                        .child_by_field_name("size")
13572                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13573                        .unwrap_or_default();
13574                    suffix.push('[');
13575                    suffix.push_str(&size);
13576                    suffix.push(']');
13577                }
13578                "parenthesized_declarator" => {}
13579                _ => unreachable!("validated recovered field declarator wrapper"),
13580            }
13581            current = current
13582                .child_by_field_name("declarator")
13583                .expect("recovered field wrapper has an inner declarator");
13584        }
13585        let separator = if prefix.is_empty() { "" } else { " " };
13586        return format!("{type_text} {prefix}{separator}{name}{suffix};");
13587    }
13588    if let Some(signature) =
13589        render_recovered_macro_qualified_field_signature(node, declarator, source)
13590    {
13591        return signature;
13592    }
13593    let prefix = cpp_declaration_prefix(node, source);
13594    let name = extract_variable_name(declarator, source).unwrap_or_default();
13595    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13596    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13597        || (prefix.ends_with('&') && raw_suffix == "&")
13598    {
13599        String::new()
13600    } else {
13601        raw_suffix
13602    };
13603
13604    let mut rendered = if suffix.is_empty() {
13605        format!("{prefix} {name}")
13606    } else if suffix.starts_with('*') || suffix.starts_with('&') {
13607        format!("{prefix}{suffix} {name}")
13608    } else if suffix.starts_with('[') || suffix.starts_with('(') {
13609        format!("{prefix} {name}{suffix}")
13610    } else {
13611        format!("{prefix} {suffix}{name}")
13612    };
13613    rendered = collapse_cpp_whitespace(&rendered);
13614
13615    // Check the terminal token directly instead of normalizing the whole
13616    // declaration, which can copy a large initializer for each field.
13617    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13618        format!("{rendered} = {initializer};")
13619    } else if cpp_direct_semicolon(node).is_some() {
13620        format!("{rendered};")
13621    } else {
13622        rendered
13623    }
13624}
13625
13626fn render_recovered_macro_qualified_field_signature(
13627    node: Node<'_>,
13628    declarator: Node<'_>,
13629    source: &str,
13630) -> Option<String> {
13631    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13632    if !recovered
13633        .iter()
13634        .any(|candidate| same_node(*candidate, declarator))
13635    {
13636        return None;
13637    }
13638    let pseudo_declarator = node.child_by_field_name("declarator")?;
13639    let mut cursor = node.walk();
13640    let clause = node
13641        .named_children(&mut cursor)
13642        .find(|child| child.kind() == "bitfield_clause")?;
13643    let mut cursor = clause.walk();
13644    let error = clause
13645        .named_children(&mut cursor)
13646        .find(|child| child.kind() == "ERROR")?;
13647    let qualified_type =
13648        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13649    let prefix = cpp_declaration_prefix(node, source);
13650    let name = extract_variable_name(declarator, source)?;
13651    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13652    let mut rendered = if suffix.is_empty() {
13653        format!("{prefix} {qualified_type} {name}")
13654    } else {
13655        format!("{prefix} {qualified_type} {suffix} {name}")
13656    };
13657    rendered = collapse_cpp_whitespace(&rendered);
13658
13659    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13660        Some(format!(
13661            "{rendered} = {};",
13662            normalize_cpp_whitespace(node_text(initializer, source))
13663        ))
13664    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13665        Some(format!("{rendered} = {initializer};"))
13666    } else {
13667        Some(format!("{rendered};"))
13668    }
13669}
13670
13671fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13672    match node.kind() {
13673        "pointer_expression" => {
13674            let operator = node
13675                .child_by_field_name("operator")
13676                .or_else(|| node.child(0))
13677                .map(|operator| node_text(operator, source))
13678                .unwrap_or("*");
13679            let argument = node
13680                .child_by_field_name("argument")
13681                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13682                .unwrap_or_default();
13683            format!("{operator}{argument}")
13684        }
13685        "unary_expression" => {
13686            let operator = node
13687                .child_by_field_name("operator")
13688                .or_else(|| node.child(0))
13689                .map(|operator| node_text(operator, source))
13690                .unwrap_or_default();
13691            let argument = node
13692                .child_by_field_name("argument")
13693                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13694                .unwrap_or_default();
13695            format!("{operator}{argument}")
13696        }
13697        "identifier" | "field_identifier" => String::new(),
13698        _ => cpp_declarator_suffix_without_name(node, source),
13699    }
13700}
13701
13702fn recovered_macro_qualified_field_initializer<'tree>(
13703    clause: Node<'tree>,
13704    declarator: Node<'tree>,
13705) -> Option<Node<'tree>> {
13706    let mut stack = vec![clause];
13707    while let Some(current) = stack.pop() {
13708        if current.kind() == "assignment_expression"
13709            && current
13710                .child_by_field_name("left")
13711                .is_some_and(|left| same_node(left, declarator))
13712        {
13713            return current.child_by_field_name("right");
13714        }
13715        let mut cursor = current.walk();
13716        stack.extend(current.named_children(&mut cursor));
13717    }
13718    None
13719}
13720
13721fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13722    let text = node_text(node, source);
13723    let mut cursor = node.walk();
13724    let first_declarator = node.named_children(&mut cursor).find(|child| {
13725        matches!(
13726            child.kind(),
13727            "init_declarator"
13728                | "identifier"
13729                | "field_identifier"
13730                | "pointer_declarator"
13731                | "reference_declarator"
13732                | "array_declarator"
13733                | "function_declarator"
13734        )
13735    });
13736    let prefix = if let Some(first_declarator) = first_declarator {
13737        let end = first_declarator
13738            .start_byte()
13739            .saturating_sub(node.start_byte());
13740        let mut prefix = text.get(..end).unwrap_or(text).to_string();
13741        let declarator_suffix = match first_declarator.kind() {
13742            "init_declarator" => first_declarator
13743                .child_by_field_name("declarator")
13744                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13745                .unwrap_or_default(),
13746            _ => cpp_declarator_suffix_without_name(first_declarator, source),
13747        };
13748        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13749            prefix.push_str(&declarator_suffix);
13750        }
13751        return collapse_cpp_whitespace(&prefix)
13752            .trim_end_matches(',')
13753            .trim_end_matches(';')
13754            .trim()
13755            .to_string();
13756    } else {
13757        text
13758    };
13759    collapse_cpp_whitespace(prefix)
13760        .trim_end_matches(',')
13761        .trim_end_matches(';')
13762        .trim()
13763        .to_string()
13764}
13765
13766fn cpp_preserved_initializer(
13767    declaration_node: Node<'_>,
13768    declarator: Node<'_>,
13769    source: &str,
13770) -> Option<String> {
13771    let name = extract_variable_name(declarator, source)?;
13772    let mut cursor = declaration_node.walk();
13773    for child in declaration_node.named_children(&mut cursor) {
13774        if child.kind() != "init_declarator" {
13775            continue;
13776        }
13777        let Some(inner) = child.child_by_field_name("declarator") else {
13778            continue;
13779        };
13780        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13781            continue;
13782        }
13783        let value = child.child_by_field_name("value")?;
13784        let kind = value.kind();
13785        if matches!(
13786            kind,
13787            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13788        ) {
13789            return Some(normalize_cpp_whitespace(node_text(value, source)));
13790        }
13791        break;
13792    }
13793    // The current grammar keeps a member initializer as a `default_value`
13794    // field beside the declarator rather than inside an `init_declarator`.
13795    if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13796        && let Some(value) = equals.next_sibling().filter(|node| {
13797            declaration_node
13798                .child_by_field_name("default_value")
13799                .is_some_and(|default| same_node(default, *node))
13800        })
13801    {
13802        let kind = value.kind();
13803        if matches!(
13804            kind,
13805            "number_literal"
13806                | "float_literal"
13807                | "char_literal"
13808                | "true"
13809                | "false"
13810                | "null"
13811                | "nullptr"
13812                | "identifier"
13813        ) {
13814            return Some(normalize_cpp_whitespace(node_text(value, source)));
13815        }
13816    }
13817    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13818    let pattern = format!(
13819        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13820        regex::escape(&name)
13821    );
13822    Regex::new(&pattern)
13823        .ok()
13824        .and_then(|regex| regex.captures(&declaration_text))
13825        .and_then(|captures| captures.get(1))
13826        .map(|value| value.as_str().to_string())
13827}
13828
13829fn render_cpp_function_display_signature_from_node<'tree>(
13830    node: Node<'tree>,
13831    source: &str,
13832    template_signature: Option<&str>,
13833    has_body: bool,
13834    ancestry: &ParentIndex<'tree>,
13835) -> String {
13836    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13837    let parent_text = node_text(root, source);
13838    let body_local_start = root
13839        .child_by_field_name("body")
13840        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13841        .unwrap_or(parent_text.len());
13842    let display = parent_text
13843        .get(..body_local_start)
13844        .unwrap_or(parent_text)
13845        .trim()
13846        .trim();
13847    let display = if let Some(template_signature) = template_signature {
13848        if display.starts_with("template ") {
13849            display.to_string()
13850        } else {
13851            format!("template {template_signature} {display}")
13852        }
13853    } else {
13854        display.to_string()
13855    };
13856    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13857    if has_body {
13858        format!("{display} {{...}}")
13859    } else {
13860        format!("{display};")
13861    }
13862}
13863
13864fn cpp_template_signature(
13865    template_node: Node<'_>,
13866    declaration_child: Node<'_>,
13867    source: &str,
13868) -> Option<String> {
13869    let text = source
13870        .get(template_node.start_byte()..declaration_child.start_byte())
13871        .unwrap_or("");
13872    let text = normalize_cpp_whitespace(text);
13873    let start = text.find('<')?;
13874    let end = text.rfind('>')?;
13875    if end < start {
13876        return None;
13877    }
13878    Some(text[start..=end].to_string())
13879}
13880
13881struct RecoveredFragmentedPartialSpecialization<'tree> {
13882    declaration_node: Node<'tree>,
13883    name: String,
13884    range: Range,
13885    prefix_members: Vec<Node<'tree>>,
13886    member_siblings: Vec<Node<'tree>>,
13887    following_declarations: Vec<Node<'tree>>,
13888}
13889
13890struct RecoveredFragmentedPreprocessorClass<'tree> {
13891    declaration_node: Node<'tree>,
13892    class_node: Node<'tree>,
13893    body: Node<'tree>,
13894    name: String,
13895    range: Range,
13896    tail_members: Vec<Node<'tree>>,
13897    member_siblings: Vec<Node<'tree>>,
13898}
13899
13900/// Recover a class whose preprocessor-fragmented parse closes at an early
13901/// member body and publishes the remaining in-class declarations as siblings
13902/// of the surrounding alternative. Primary classes are admitted only when an
13903/// earlier branch contains the matching bodyless declaration and the class
13904/// node retains the displaced `#endif`. Partial specializations instead carry
13905/// their identity structurally in the `template_type` name and template
13906/// metadata. Retain the original AST nodes and re-own only the siblings through
13907/// the displaced structural `};` terminator.
13908fn recover_fragmented_preprocessor_class<'tree>(
13909    template_node: Node<'tree>,
13910    source: &str,
13911    ancestry: &ParentIndex<'tree>,
13912) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13913    let alternative = ancestry.parent(template_node)?;
13914    if alternative.kind() != "preproc_else" {
13915        return None;
13916    }
13917    let conditional = alternative.parent()?;
13918    if conditional.kind() != "preproc_if" {
13919        return None;
13920    }
13921    let declaration_node = template_node
13922        .named_children(&mut template_node.walk())
13923        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13924    let class_node = declaration_node
13925        .named_children(&mut declaration_node.walk())
13926        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13927    let body = cpp_body_node(class_node)?;
13928    if class_node.end_byte() >= declaration_node.end_byte() {
13929        return None;
13930    }
13931    let name = class_like_name(class_node, source, ancestry)?;
13932    let is_partial_specialization = class_node
13933        .child_by_field_name("name")
13934        .is_some_and(|class_name| class_name.kind() == "template_type");
13935    if is_partial_specialization {
13936        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13937        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13938            return None;
13939        }
13940    } else {
13941        if !class_has_displaced_preprocessor_terminator(class_node) {
13942            return None;
13943        }
13944        let matching_other_branch = conditional
13945            .named_children(&mut conditional.walk())
13946            .take_while(|child| !same_node(*child, alternative))
13947            .filter(|child| child.kind() == "template_declaration")
13948            .filter_map(first_class_like_child)
13949            .any(|candidate| {
13950                cpp_body_node(candidate).is_none()
13951                    && class_like_name(candidate, source, ancestry).as_deref()
13952                        == Some(name.as_str())
13953            });
13954        if !matching_other_branch {
13955            return None;
13956        }
13957    }
13958
13959    let mut tail_members = Vec::new();
13960    let mut saw_class = false;
13961    let mut declaration_cursor = declaration_node.walk();
13962    for child in declaration_node.named_children(&mut declaration_cursor) {
13963        if same_node(child, class_node) {
13964            saw_class = true;
13965        } else if saw_class {
13966            tail_members.push(child);
13967        }
13968    }
13969
13970    let mut member_siblings = Vec::new();
13971    let mut saw_template = false;
13972    let mut terminator = None;
13973    for index in 0..alternative.child_count() {
13974        let Some(child) = alternative.child(index) else {
13975            continue;
13976        };
13977        if same_node(child, template_node) {
13978            saw_template = true;
13979            continue;
13980        }
13981        if !saw_template {
13982            continue;
13983        }
13984        if displaced_fragmented_class_terminator(alternative, index) {
13985            terminator = alternative.child(index + 1);
13986            break;
13987        }
13988        if child.is_named() {
13989            member_siblings.push(child);
13990        }
13991    }
13992    let terminator = terminator?;
13993    Some(RecoveredFragmentedPreprocessorClass {
13994        declaration_node,
13995        class_node,
13996        body,
13997        name,
13998        range: Range {
13999            start_byte: class_node.start_byte(),
14000            end_byte: terminator.end_byte(),
14001            start_line: class_node.start_position().row + 1,
14002            end_line: terminator.end_position().row + 1,
14003        },
14004        tail_members,
14005        member_siblings,
14006    })
14007}
14008
14009fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
14010    (0..class_node.child_count()).any(|index| {
14011        class_node.child(index).is_some_and(|child| {
14012            child.kind() == "ERROR"
14013                && (0..child.child_count()).any(|error_index| {
14014                    child
14015                        .child(error_index)
14016                        .is_some_and(|token| token.kind() == "#endif")
14017                })
14018        })
14019    })
14020}
14021
14022/// The real `#endif` that tree-sitter consumed inside an error subtree.
14023///
14024/// A preprocessor directive inside a malformed array bound can cause later
14025/// declarations to remain children of the conditional. The non-missing token
14026/// still gives the exact structured boundary. Ignore nested conditionals and
14027/// select the last unpaired error-owned token. Tree-sitter can pair a later
14028/// outer `#endif` with this conditional, so the direct terminator is not
14029/// necessarily missing.
14030pub fn cpp_displaced_preprocessor_terminator<'tree>(
14031    conditional: Node<'tree>,
14032) -> Option<Node<'tree>> {
14033    if !conditional.has_error() {
14034        return None;
14035    }
14036    let has_concrete_direct_terminator = conditional
14037        .child_count()
14038        .checked_sub(1)
14039        .and_then(|index| conditional.child(index))
14040        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
14041    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
14042        // A structured alternative proves that the direct `#endif` closes
14043        // this family. An error-owned terminator inside either branch belongs
14044        // to a damaged nested conditional, not to this one.
14045        return None;
14046    }
14047    // Pair concrete directive tokens while walking the recovered subtree. An
14048    // inner conditional may be represented only by its `#ifndef`/`#endif`
14049    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
14050    // sufficient: an inner `#endif` can otherwise be mistaken for this
14051    // conditional's displaced terminator.
14052    let mut displaced = None;
14053    let mut conditional_depth = 0usize;
14054    let mut stack = children_iter(conditional)
14055        .map(|child| (child, false))
14056        .collect::<Vec<_>>();
14057    stack.reverse();
14058    while let Some((node, inside_error)) = stack.pop() {
14059        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14060            continue;
14061        }
14062        if node != conditional
14063            && matches!(
14064                node.kind(),
14065                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
14066            )
14067        {
14068            continue;
14069        }
14070        let inside_error = inside_error || node.kind() == "ERROR";
14071        match node.kind() {
14072            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
14073                conditional_depth += 1;
14074            }
14075            "#endif" if !node.is_missing() => {
14076                if conditional_depth == 0
14077                    && inside_error
14078                    && displaced
14079                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
14080                {
14081                    displaced = Some(node);
14082                }
14083                conditional_depth = conditional_depth.saturating_sub(1);
14084            }
14085            _ => {}
14086        }
14087        let first_pushed = stack.len();
14088        stack.extend(children_iter(node).map(|child| (child, inside_error)));
14089        stack[first_pushed..].reverse();
14090    }
14091    displaced
14092}
14093
14094/// The first `#else`/`#elif` of `conditional` that tree-sitter displaced into
14095/// an `ERROR` inside the conditional's own branch.
14096///
14097/// The macro-declaration grammar can read an unterminated end sentinel, the
14098/// branch directive and the next branch's begin sentinel as one declaration
14099/// (`END_NS #else BEGIN_NS class C {};`, #6325): the `#else` becomes an
14100/// `ERROR` child and the conditional loses its `alternative`. The token is
14101/// still the structured branch boundary. Nested conditionals own their own
14102/// directives, including ones represented only by error-owned `#if`/`#endif`
14103/// tokens, so the walk pairs those the same way
14104/// [`cpp_displaced_preprocessor_terminator`] does.
14105pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14106    if !conditional.has_error() {
14107        return None;
14108    }
14109    let alternative = conditional.child_by_field_name("alternative");
14110    let mut conditional_depth = 0usize;
14111    let mut stack = children_iter(conditional)
14112        .filter(|child| Some(*child) != alternative)
14113        .map(|child| (child, false))
14114        .collect::<Vec<_>>();
14115    stack.reverse();
14116    while let Some((node, inside_error)) = stack.pop() {
14117        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14118            continue;
14119        }
14120        if matches!(
14121            node.kind(),
14122            "preproc_if"
14123                | "preproc_ifdef"
14124                | "preproc_ifndef"
14125                | "preproc_elif"
14126                | "preproc_elifdef"
14127                | "preproc_else"
14128        ) {
14129            continue;
14130        }
14131        let inside_error = inside_error || node.kind() == "ERROR";
14132        match node.kind() {
14133            "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14134            "#endif" if !node.is_missing() => {
14135                conditional_depth = conditional_depth.saturating_sub(1);
14136            }
14137            "#else" | "#elif" | "#elifdef" | "#elifndef"
14138                if inside_error && conditional_depth == 0 && !node.is_missing() =>
14139            {
14140                return Some(node);
14141            }
14142            _ => {}
14143        }
14144        let first_pushed = stack.len();
14145        stack.extend(children_iter(node).map(|child| (child, inside_error)));
14146        stack[first_pushed..].reverse();
14147    }
14148    None
14149}
14150
14151/// The effective end of a conditional whose real terminator tree-sitter
14152/// displaced into declaration recovery.
14153///
14154/// Most damaged conditionals retain a concrete `#endif` token below an
14155/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
14156/// boundary. A preprocessor family that selects the middle of a declaration
14157/// can lose the directive tokens entirely. In that shape tree-sitter leaves
14158/// the declaration's `typedef` token as the sole child of the immediately
14159/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
14160/// declarator name inside the conditional's first declaration. The declaration
14161/// end is then the smallest structured boundary that contains the whole split
14162/// declaration.
14163#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14164pub struct CppDisplacedPreprocessorBoundary {
14165    pub end_byte: usize,
14166    pub end_line: usize,
14167}
14168
14169pub fn cpp_displaced_preprocessor_boundary(
14170    conditional: Node<'_>,
14171) -> Option<CppDisplacedPreprocessorBoundary> {
14172    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14173        return Some(CppDisplacedPreprocessorBoundary {
14174            end_byte: terminator.end_byte(),
14175            end_line: terminator.end_position().row + 1,
14176        });
14177    }
14178    if let Some(declaration) = displaced_split_declaration(conditional) {
14179        return Some(CppDisplacedPreprocessorBoundary {
14180            end_byte: declaration.end_byte(),
14181            end_line: declaration.end_position().row + 1,
14182        });
14183    }
14184    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14185        return Some(CppDisplacedPreprocessorBoundary {
14186            end_byte: terminator.end_byte(),
14187            end_line: terminator.end_position().row + 1,
14188        });
14189    }
14190    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14191        return Some(CppDisplacedPreprocessorBoundary {
14192            end_byte: terminator.end_byte(),
14193            end_line: terminator.end_position().row + 1,
14194        });
14195    }
14196    None
14197}
14198
14199/// Recover an outer terminator that tree-sitter assigned to a damaged nested
14200/// conditional. This occurs when a split construct such as `extern "C"`
14201/// consumes the nested `#endif` inside an error node: the nested conditional's
14202/// direct terminator is then the outer conditional's real terminator, while
14203/// the outer node ends with a missing token and absorbs later declarations.
14204fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14205    if !conditional.has_error()
14206        || conditional.child_by_field_name("alternative").is_some()
14207        || conditional
14208            .child(conditional.child_count().saturating_sub(1))
14209            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14210    {
14211        return None;
14212    }
14213    let mut recovered = None;
14214    for nested in named_children_iter(conditional) {
14215        if !matches!(
14216            nested.kind(),
14217            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14218        ) || nested.child_by_field_name("alternative").is_some()
14219        {
14220            continue;
14221        }
14222        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14223            continue;
14224        };
14225        if direct.kind() != "#endif" || direct.is_missing() {
14226            continue;
14227        }
14228        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14229            continue;
14230        };
14231        if displaced.end_byte() >= direct.start_byte() {
14232            continue;
14233        }
14234        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14235            recovered = Some(direct);
14236        }
14237    }
14238    recovered
14239}
14240
14241fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14242    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14243        return None;
14244    }
14245    let mut cursor = conditional.walk();
14246    let declarations = conditional
14247        .named_children(&mut cursor)
14248        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14249        .collect::<Vec<_>>();
14250    let declaration = *declarations.first()?;
14251    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14252        return None;
14253    }
14254    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14255    let mut terminator = None;
14256    let mut stack = (0..declaration.child_count())
14257        .filter_map(|index| declaration.child(index))
14258        .filter(|child| child.start_byte() < declarator_start)
14259        .map(|child| (child, false))
14260        .collect::<Vec<_>>();
14261    while let Some((node, inside_error)) = stack.pop() {
14262        let inside_error = inside_error || node.kind() == "ERROR";
14263        if inside_error && node.kind() == "#endif" && !node.is_missing() {
14264            terminator = Some(node);
14265            continue;
14266        }
14267        for index in 0..node.child_count() {
14268            if let Some(child) = node.child(index)
14269                && child.start_byte() < declarator_start
14270            {
14271                stack.push((child, inside_error));
14272            }
14273        }
14274    }
14275    terminator
14276}
14277
14278fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14279    if !conditional.has_error()
14280        || conditional.child_by_field_name("alternative").is_some()
14281        || conditional
14282            .prev_named_sibling()
14283            .filter(|sibling| {
14284                sibling.kind() == "ERROR"
14285                    && sibling.child_count() == 1
14286                    && sibling
14287                        .child(0)
14288                        .is_some_and(|child| child.kind() == "typedef")
14289            })
14290            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14291            .is_none()
14292    {
14293        return None;
14294    }
14295    let mut cursor = conditional.walk();
14296    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14297    let declaration_index = children
14298        .iter()
14299        .position(|child| child.kind() == "declaration" && child.has_error())?;
14300    let declaration = children[declaration_index];
14301    if !children
14302        .iter()
14303        .skip(declaration_index + 1)
14304        .any(|child| child.end_byte() > declaration.end_byte())
14305    {
14306        return None;
14307    }
14308    let declarator = declaration.child_by_field_name("declarator")?;
14309    let mut error_end = None;
14310    let mut names = Vec::new();
14311    let mut stack = vec![declarator];
14312    while let Some(node) = stack.pop() {
14313        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14314            error_end =
14315                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14316            continue;
14317        }
14318        if matches!(node.kind(), "identifier" | "type_identifier") {
14319            names.push(node.start_byte());
14320        }
14321        push_named_children_reversed(node, &mut stack);
14322    }
14323    let error_end = error_end?;
14324    names
14325        .into_iter()
14326        .any(|start| start >= error_end)
14327        .then_some(declaration)
14328}
14329
14330fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14331    let Some(error) = parent.child(error_index) else {
14332        return false;
14333    };
14334    if error.kind() != "ERROR"
14335        || error.child_count() != 1
14336        || error.child(0).is_none_or(|child| child.kind() != "}")
14337    {
14338        return false;
14339    }
14340    let Some(semicolon) = parent.child(error_index + 1) else {
14341        return false;
14342    };
14343    semicolon.kind() == "expression_statement"
14344        && semicolon.child_count() == 1
14345        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14346}
14347
14348/// Locate the real end of a class-like declaration when a macro invocation
14349/// without a source semicolon absorbs the class's `};` into its parsed field.
14350/// The grammar then keeps following namespace declarations as later children
14351/// of the same field list. The direct ERROR-plus-semicolon pair proves the
14352/// boundary structurally; no source-text delimiter scan is needed.
14353fn displaced_macro_class_tail(
14354    declaration_node: Node<'_>,
14355    body: Node<'_>,
14356    source: &str,
14357) -> Option<DisplacedMacroClassTail> {
14358    if !matches!(
14359        declaration_node.kind(),
14360        "class_specifier" | "struct_specifier" | "union_specifier"
14361    ) || body.kind() != "field_declaration_list"
14362    {
14363        return None;
14364    }
14365
14366    let child_count = body.named_child_count();
14367    for index in 0..child_count {
14368        let child = body.named_child(index)?;
14369        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14370            continue;
14371        };
14372        let split_index = index + 1;
14373        if split_index >= child_count {
14374            return None;
14375        }
14376        let mut cursor = body.walk();
14377        if !body
14378            .named_children(&mut cursor)
14379            .skip(split_index)
14380            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14381        {
14382            return None;
14383        }
14384        return Some(DisplacedMacroClassTail {
14385            split_index,
14386            class_range: Range {
14387                start_byte: declaration_node.start_byte(),
14388                end_byte: terminator.end_byte(),
14389                start_line: declaration_node.start_position().row + 1,
14390                end_line: terminator.end_position().row + 1,
14391            },
14392        });
14393    }
14394    None
14395}
14396
14397fn displaced_macro_field_terminator<'tree>(
14398    field: Node<'tree>,
14399    source: &str,
14400) -> Option<Node<'tree>> {
14401    if field.kind() != "field_declaration" {
14402        return None;
14403    }
14404    // The older grammar reads the unterminated `MACRO(arg)` invocation as a
14405    // macro-token type and a `parenthesized_declarator`; the macro-declaration
14406    // grammar reads the whole invocation as a `macro_type_specifier`.
14407    let macro_type = field.child_by_field_name("type")?;
14408    let macro_name = match macro_type.kind() {
14409        "type_identifier"
14410            if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14411        {
14412            macro_type
14413        }
14414        "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14415        _ => return None,
14416    };
14417    if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14418        return None;
14419    }
14420    for index in 0..field.child_count() {
14421        let error = field.child(index)?;
14422        if error.kind() != "ERROR"
14423            || error.child_count() != 1
14424            || error.child(0).is_none_or(|child| child.kind() != "}")
14425        {
14426            continue;
14427        }
14428        let semicolon = field.child(index + 1)?;
14429        if semicolon.kind() == ";" {
14430            return Some(semicolon);
14431        }
14432    }
14433    None
14434}
14435
14436fn recover_fragmented_partial_specialization<'tree>(
14437    template_node: Node<'tree>,
14438    declaration_child: Node<'tree>,
14439    source: &str,
14440    ancestry: &ParentIndex<'tree>,
14441) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14442    if declaration_child.kind() != "function_definition" {
14443        return None;
14444    }
14445    let class_node = declaration_child.child_by_field_name("type")?;
14446    if !matches!(
14447        class_node.kind(),
14448        "class_specifier" | "struct_specifier" | "union_specifier"
14449    ) || !class_node
14450        .child_by_field_name("name")
14451        .and_then(|name| direct_identifier_name(name, source))
14452        .is_some_and(|name| cpp_export_macro_token(&name))
14453    {
14454        return None;
14455    }
14456    let declarator = declaration_child.child_by_field_name("declarator")?;
14457    if declarator.kind() != "template_function" {
14458        return None;
14459    }
14460    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14461    if metadata.specialization_arguments.is_empty() {
14462        return None;
14463    }
14464    let body = declaration_child.child_by_field_name("body")?;
14465    if body.kind() != "compound_statement" {
14466        return None;
14467    }
14468    let complete_prefix = body.named_child(0).filter(|first| {
14469        first.kind() == "labeled_statement"
14470            && first.has_error()
14471            && first
14472                .named_child(first.named_child_count().saturating_sub(1))
14473                .is_some_and(recovered_declaration_has_class_terminator)
14474    });
14475    let complete_body = complete_prefix.is_some();
14476    let mut prefix_members = Vec::new();
14477    if let Some(prefix) = complete_prefix {
14478        prefix_members.push(prefix);
14479    } else {
14480        let mut body_cursor = body.walk();
14481        for child in body.named_children(&mut body_cursor) {
14482            if !is_structurally_valid_fragmented_class_prefix_member(child) {
14483                break;
14484            }
14485            prefix_members.push(child);
14486        }
14487    }
14488    let containing_declarations = template_node.parent()?;
14489    if !matches!(
14490        containing_declarations.kind(),
14491        "declaration_list" | "compound_statement"
14492    ) {
14493        return None;
14494    }
14495    let mut member_siblings = Vec::new();
14496    let mut following_declarations = Vec::new();
14497    let terminator;
14498    if complete_body {
14499        terminator = complete_prefix?;
14500        let mut cursor = body.walk();
14501        let mut after_prefix = false;
14502        for child in body.named_children(&mut cursor) {
14503            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14504                after_prefix = true;
14505            } else if after_prefix {
14506                following_declarations.push(child);
14507            }
14508        }
14509    } else {
14510        let mut found_template = false;
14511        let mut cursor = containing_declarations.walk();
14512        let mut class_terminator = None;
14513        for child in containing_declarations.children(&mut cursor) {
14514            if same_node(child, template_node) {
14515                found_template = true;
14516                continue;
14517            }
14518            if found_template && child.kind() == "}" {
14519                class_terminator = Some(child);
14520                break;
14521            }
14522            // A namespace can never be a class member: reaching one before the
14523            // terminator proves the class's true close was swallowed upstream
14524            // and this scan has crossed into the enclosing scope, so the
14525            // recovery cannot be bounded -- continuing re-owns the namespace
14526            // block (and its template specializations) as class members under
14527            // a re-appended package, desyncing the fq boundary (#2306).
14528            if found_template && child.kind() == "namespace_definition" {
14529                return None;
14530            }
14531            if found_template && child.is_named() {
14532                member_siblings.push(child);
14533            }
14534        }
14535        terminator = class_terminator?;
14536    }
14537    let name = format!(
14538        "{}<{}>",
14539        metadata.primary_name,
14540        metadata
14541            .specialization_arguments
14542            .iter()
14543            .map(|argument| argument.text.as_str())
14544            .collect::<Vec<_>>()
14545            .join(", ")
14546    );
14547    Some(RecoveredFragmentedPartialSpecialization {
14548        declaration_node: declaration_child,
14549        name,
14550        range: Range {
14551            start_byte: declaration_child.start_byte(),
14552            end_byte: terminator.end_byte(),
14553            start_line: declaration_child.start_position().row + 1,
14554            end_line: terminator.end_position().row + 1,
14555        },
14556        prefix_members,
14557        member_siblings,
14558        following_declarations,
14559    })
14560}
14561
14562/// Whether `node` is the function-shaped parser wrapper whose body was
14563/// recovered as a fragmented partial-specialization class body.
14564///
14565/// Consumers that walk lexical function scopes must use the same complete
14566/// structural proof as declaration extraction. Otherwise member callables in
14567/// the recovered body are mistaken for function-local declarations.
14568pub fn is_recovered_fragmented_partial_specialization_container(
14569    node: Node<'_>,
14570    source: &str,
14571) -> bool {
14572    let Some(template) = node
14573        .parent()
14574        .filter(|parent| parent.kind() == "template_declaration")
14575    else {
14576        return false;
14577    };
14578    let mut root = template;
14579    while let Some(parent) = root.parent() {
14580        root = parent;
14581    }
14582    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14583        .is_some()
14584}
14585
14586fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14587    if declaration.kind() != "declaration" {
14588        return false;
14589    }
14590    // With an export macro between `class` and its name, tree-sitter folds a
14591    // complete class body into a function-shaped declaration. The class's own
14592    // `};` remains structurally identifiable as a direct ERROR child holding
14593    // `}`, immediately followed by the declaration's direct `;` child.
14594    (0..declaration.child_count().saturating_sub(1)).any(|index| {
14595        let Some(error) = declaration.child(index) else {
14596            return false;
14597        };
14598        error.kind() == "ERROR"
14599            && error.child_count() == 1
14600            && error.child(0).is_some_and(|child| child.kind() == "}")
14601            && declaration
14602                .child(index + 1)
14603                .is_some_and(|child| child.kind() == ";")
14604    })
14605}
14606
14607fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14608    if node.has_error() {
14609        return false;
14610    }
14611    match node.kind() {
14612        "declaration"
14613        | "field_declaration"
14614        | "alias_declaration"
14615        | "type_definition"
14616        | "static_assert_declaration" => true,
14617        "labeled_statement" => node
14618            .named_child(node.named_child_count().saturating_sub(1))
14619            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14620        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14621            matches!(
14622                child.kind(),
14623                "declaration"
14624                    | "field_declaration"
14625                    | "alias_declaration"
14626                    | "type_definition"
14627                    | "function_definition"
14628            )
14629        }),
14630        _ => false,
14631    }
14632}
14633
14634fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14635    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14636        .then(|| node.child_by_field_name("declarator"))
14637        .flatten()
14638        .and_then(|declarator| extract_variable_name(declarator, source))
14639}
14640
14641use crate::graph::syntax::has_function_scope_ancestor;
14642
14643fn cpp_template_metadata<'tree>(
14644    template_node: Node<'tree>,
14645    declaration_child: Node<'tree>,
14646    source: &str,
14647    ancestry: &ParentIndex<'tree>,
14648) -> Option<CppTemplateMetadata> {
14649    let parameters_node = template_node.child_by_field_name("parameters")?;
14650    let name_node = cpp_templated_class_name_node(declaration_child)?;
14651    let primary_node = match name_node.kind() {
14652        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14653        _ => name_node,
14654    };
14655    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14656    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14657        return None;
14658    }
14659
14660    let mut parameter_nodes = Vec::new();
14661    let mut parameter_names = Vec::new();
14662    let mut cursor = parameters_node.walk();
14663    for parameter in parameters_node.named_children(&mut cursor) {
14664        if !matches!(
14665            parameter.kind(),
14666            "type_parameter_declaration"
14667                | "optional_type_parameter_declaration"
14668                | "variadic_type_parameter_declaration"
14669                | "template_template_parameter_declaration"
14670                | "parameter_declaration"
14671                | "optional_parameter_declaration"
14672                | "variadic_parameter_declaration"
14673        ) {
14674            continue;
14675        }
14676        let index = parameter_nodes.len();
14677        // An unnamed parameter still contributes template arity and kind. Use
14678        // an impossible C++ identifier so positional reconciliation can bind
14679        // it without making source expressions refer to a name that was not
14680        // written.
14681        let name = cpp_template_parameter_name(parameter, source)
14682            .unwrap_or_else(|| format!("<anonymous:{index}>"));
14683        parameter_names.push(name);
14684        parameter_nodes.push(parameter);
14685    }
14686    let parameters = parameter_nodes
14687        .into_iter()
14688        .zip(parameter_names.iter().cloned())
14689        .map(|(parameter, name)| CppTemplateParameterMetadata {
14690            name,
14691            kind: cpp_template_parameter_kind(parameter),
14692            variadic: matches!(
14693                parameter.kind(),
14694                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14695            ),
14696            default: cpp_template_parameter_default_expression(
14697                parameter,
14698                source,
14699                &parameter_names,
14700                ancestry,
14701            ),
14702        })
14703        .collect();
14704    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14705        Vec::new()
14706    } else {
14707        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
14708            .unwrap_or_default()
14709    };
14710    let alias_target = (declaration_child.kind() == "alias_declaration")
14711        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
14712        .flatten();
14713    Some(CppTemplateMetadata {
14714        primary_name,
14715        primary_fq_name: String::new(),
14716        parameters,
14717        specialization_arguments,
14718        alias_target,
14719    })
14720}
14721
14722fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14723    match node.kind() {
14724        "class_specifier" | "struct_specifier" | "union_specifier" => {
14725            node.child_by_field_name("name")
14726        }
14727        "function_definition" => {
14728            let declarator = node.child_by_field_name("declarator")?;
14729            if matches!(declarator.kind(), "identifier" | "template_function") {
14730                Some(declarator)
14731            } else {
14732                None
14733            }
14734        }
14735        "alias_declaration" => node.child_by_field_name("name"),
14736        _ => None,
14737    }
14738}
14739
14740fn cpp_template_alias_target<'tree>(
14741    alias: Node<'tree>,
14742    source: &str,
14743    parameter_names: &[String],
14744    ancestry: &ParentIndex<'tree>,
14745) -> Option<CppTemplateAliasTargetMetadata> {
14746    let mut type_node = alias.child_by_field_name("type")?;
14747    while type_node.kind() == "type_descriptor" {
14748        type_node = type_node.child_by_field_name("type")?;
14749    }
14750    let global = type_node.child_by_field_name("scope").is_none()
14751        && type_node.child(0).is_some_and(|child| child.kind() == "::");
14752    let mut components = Vec::new();
14753    cpp_template_target_components(type_node, source, &mut components)?;
14754    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14755    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14756        components,
14757        global,
14758        arguments,
14759    })
14760}
14761
14762fn cpp_template_target_components(
14763    node: Node<'_>,
14764    source: &str,
14765    out: &mut Vec<String>,
14766) -> Option<()> {
14767    // Delay reading the terminal name until the scope has been processed,
14768    // preserving the partial output even when a later component is missing.
14769    enum Component<'tree> {
14770        Node(Node<'tree>),
14771        Name(Node<'tree>),
14772    }
14773    let mut stack = vec![Component::Node(node)];
14774    while let Some(component) = stack.pop() {
14775        let node = match component {
14776            Component::Node(node) => node,
14777            Component::Name(node) => node.child_by_field_name("name")?,
14778        };
14779        match node.kind() {
14780            "identifier" | "namespace_identifier" | "type_identifier" => {
14781                out.push(node_text(node, source).to_string());
14782            }
14783            "template_type" => stack.push(Component::Name(node)),
14784            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14785                stack.push(Component::Name(node));
14786                if let Some(scope) = node.child_by_field_name("scope") {
14787                    stack.push(Component::Node(scope));
14788                }
14789            }
14790            _ => return None,
14791        }
14792    }
14793    Some(())
14794}
14795
14796fn cpp_template_argument_expressions<'tree>(
14797    mut node: Node<'tree>,
14798    source: &str,
14799    parameter_names: &[String],
14800    ancestry: &ParentIndex<'tree>,
14801) -> Option<Vec<CppTemplateExpression>> {
14802    loop {
14803        match node.kind() {
14804            "template_type" | "template_function" => {
14805                let arguments = node.child_by_field_name("arguments")?;
14806                let mut cursor = arguments.walk();
14807                return Some(
14808                    arguments
14809                        .named_children(&mut cursor)
14810                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14811                        .map(|argument| {
14812                            cpp_template_expression(argument, source, parameter_names, ancestry)
14813                        })
14814                        .collect(),
14815                );
14816            }
14817            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14818                node = node
14819                    .child_by_field_name("name")
14820                    .or_else(|| node.child_by_field_name("type"))?;
14821            }
14822            _ => return None,
14823        }
14824    }
14825}
14826
14827fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14828    if node.kind() == "template_template_parameter_declaration" {
14829        let mut cursor = node.walk();
14830        return node
14831            .named_children(&mut cursor)
14832            .find(|child| {
14833                matches!(
14834                    child.kind(),
14835                    "type_parameter_declaration"
14836                        | "optional_type_parameter_declaration"
14837                        | "variadic_type_parameter_declaration"
14838                )
14839            })
14840            .expect("a template-template parameter has a type parameter declaration");
14841    }
14842    node
14843}
14844
14845pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14846    let node = cpp_template_parameter_declaration(node);
14847    let candidate = node
14848        .child_by_field_name("name")
14849        .or_else(|| node.child_by_field_name("declarator"))
14850        .or_else(|| cpp_recovered_type_parameter_name(node))
14851        .or_else(|| {
14852            let mut cursor = node.walk();
14853            node.named_children(&mut cursor).find(|child| {
14854                matches!(
14855                    child.kind(),
14856                    "identifier" | "type_identifier" | "field_identifier"
14857                )
14858            })
14859        })?;
14860    if let Some(name) = direct_identifier_name(candidate, source) {
14861        return Some(name);
14862    }
14863    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14864    (!name.is_empty()).then_some(name)
14865}
14866
14867pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14868    match node.kind() {
14869        "type_parameter_declaration"
14870        | "optional_type_parameter_declaration"
14871        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14872        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14873        _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14874        _ => CppTemplateParameterKind::Value,
14875    }
14876}
14877
14878/// The name of a defaulted type parameter that tree-sitter-cpp recovered as a
14879/// non-type parameter. The grammar's `default_type` cannot hold an abstract
14880/// declarator, so `typename U = T*` always recovers the `*` as an ERROR
14881/// sibling; since grammar 0.24.4 the parameter itself also becomes an
14882/// `optional_parameter_declaration` with `type: dependent_type(typename U)`,
14883/// no declarator and `default_value: T`. A valid dependent type names a
14884/// qualified type after `typename`, so a bare type identifier there is the
14885/// type parameter's name.
14886pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14887    if parameter.kind() != "optional_parameter_declaration"
14888        || parameter.child_by_field_name("declarator").is_some()
14889    {
14890        return None;
14891    }
14892    let dependent = parameter
14893        .child_by_field_name("type")
14894        .filter(|type_node| type_node.kind() == "dependent_type")?;
14895    let mut cursor = dependent.walk();
14896    let mut named = dependent
14897        .named_children(&mut cursor)
14898        .filter(|child| !child.is_extra());
14899    match (named.next(), named.next()) {
14900        (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14901        _ => None,
14902    }
14903}
14904
14905#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14906pub struct CppTemplateParameterNameMatches {
14907    pub any_parameter: bool,
14908    pub type_parameter: bool,
14909}
14910
14911pub fn cpp_template_parameter_name_matches(
14912    parameters: Node<'_>,
14913    name: &str,
14914    mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14915) -> CppTemplateParameterNameMatches {
14916    let mut matches = CppTemplateParameterNameMatches::default();
14917    let mut cursor = parameters.walk();
14918    for parameter in parameters.named_children(&mut cursor) {
14919        if parameter_name(parameter).as_deref() == Some(name) {
14920            matches.any_parameter = true;
14921            matches.type_parameter |=
14922                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14923        }
14924    }
14925    matches
14926}
14927
14928pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14929    parent.child_by_field_name("type").is_some_and(|type_node| {
14930        type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14931    })
14932}
14933
14934fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14935    let node = cpp_template_parameter_declaration(node);
14936    node.child_by_field_name("default_type")
14937        .or_else(|| node.child_by_field_name("default_value"))
14938}
14939
14940fn cpp_template_parameter_default_expression<'tree>(
14941    parameter: Node<'tree>,
14942    source: &str,
14943    parameter_names: &[String],
14944    ancestry: &ParentIndex<'tree>,
14945) -> Option<CppTemplateExpression> {
14946    let default = cpp_template_parameter_default(parameter)?;
14947    let base = cpp_template_expression(default, source, parameter_names, ancestry);
14948    let Some(pointer_error) = parameter.next_named_sibling() else {
14949        return Some(base);
14950    };
14951    // The grammar's default_type accepts a type specifier, not a complete
14952    // type-id. For `const T*` it calls `const` the type and leaves `T*` in
14953    // the next ERROR. Let the type-id grammar interpret that exact range.
14954    if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14955        && pointer_error.is_error()
14956    {
14957        let text = &source[default.start_byte()..pointer_error.end_byte()];
14958        let parsed = crate::graph::syntax::parse_sentinel(
14959            "using __bifrost_default = ",
14960            crate::graph::syntax::SentinelInput::region(text),
14961            ";",
14962        );
14963        let root = parsed.tree.root_node();
14964        if !root.has_error()
14965            && let Some(type_node) =
14966                brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
14967                    root,
14968                    |child| !child.is_extra(),
14969                )
14970                .filter(|alias| alias.kind() == "alias_declaration")
14971                .and_then(|alias| alias.child_by_field_name("type"))
14972        {
14973            let alias_ancestry = ParentIndex::new(root);
14974            return Some(cpp_template_expression(
14975                type_node,
14976                &parsed.source,
14977                parameter_names,
14978                &alias_ancestry,
14979            ));
14980        }
14981    }
14982    let Some(pointer_declarator) =
14983        recovered_abstract_pointer_declarator_term(pointer_error, source)
14984    else {
14985        return Some(base);
14986    };
14987    Some(CppTemplateExpression {
14988        text: format!(
14989            "{}{}",
14990            base.text,
14991            normalize_cpp_whitespace(node_text(pointer_error, source))
14992        ),
14993        term: CppTemplateTerm::Node {
14994            kind: "type_descriptor".to_string(),
14995            children: vec![base.term, pointer_declarator],
14996        },
14997    })
14998}
14999
15000fn recovered_abstract_pointer_declarator_term(
15001    node: Node<'_>,
15002    source: &str,
15003) -> Option<CppTemplateTerm> {
15004    if node.kind() != "ERROR" || node.child_count() == 0 {
15005        return None;
15006    }
15007    let mut children = Vec::new();
15008    for index in 0..node.child_count() {
15009        let child = node.child(index)?;
15010        if child.kind() != "*" {
15011            return None;
15012        }
15013        children.push(CppTemplateTerm::Atom {
15014            kind: "*".to_string(),
15015            text: normalize_cpp_whitespace(node_text(child, source)),
15016        });
15017    }
15018    Some(CppTemplateTerm::Node {
15019        kind: "abstract_pointer_declarator".to_string(),
15020        children,
15021    })
15022}
15023
15024fn cpp_template_expression<'tree>(
15025    node: Node<'tree>,
15026    source: &str,
15027    parameter_names: &[String],
15028    ancestry: &ParentIndex<'tree>,
15029) -> CppTemplateExpression {
15030    let text = normalize_cpp_whitespace(node_text(node, source));
15031    CppTemplateExpression {
15032        text,
15033        term: cpp_template_term(node, source, parameter_names, ancestry),
15034    }
15035}
15036
15037pub fn cpp_template_term<'tree>(
15038    node: Node<'tree>,
15039    source: &str,
15040    parameter_names: &[String],
15041    ancestry: &ParentIndex<'tree>,
15042) -> CppTemplateTerm {
15043    enum Work<'tree> {
15044        Visit(Node<'tree>),
15045        Build { kind: String, child_count: usize },
15046    }
15047
15048    let mut work = vec![Work::Visit(node)];
15049    let mut terms = Vec::new();
15050    while let Some(next) = work.pop() {
15051        match next {
15052            Work::Visit(current) => {
15053                let text = normalize_cpp_whitespace(node_text(current, source));
15054                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
15055                    terms.push(CppTemplateTerm::Parameter(text));
15056                    continue;
15057                }
15058                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
15059                    let mut cursor = current.walk();
15060                    let named = current
15061                        .named_children(&mut cursor)
15062                        .filter(|child| !child.is_extra() && child.kind() != "comment")
15063                        .collect::<Vec<_>>();
15064                    if let [child] = named.as_slice() {
15065                        work.push(Work::Visit(*child));
15066                        continue;
15067                    }
15068                }
15069                if current.child_count() == 0 {
15070                    terms.push(CppTemplateTerm::Atom {
15071                        kind: if matches!(
15072                            current.kind(),
15073                            "identifier"
15074                                | "type_identifier"
15075                                | "field_identifier"
15076                                | "namespace_identifier"
15077                        ) {
15078                            "identifier".to_string()
15079                        } else {
15080                            current.kind().to_string()
15081                        },
15082                        text,
15083                    });
15084                    continue;
15085                }
15086                let children = (0..current.child_count())
15087                    .filter_map(|index| current.child(index))
15088                    .filter(|child| !child.is_extra() && child.kind() != "comment")
15089                    .collect::<Vec<_>>();
15090                work.push(Work::Build {
15091                    kind: current.kind().to_string(),
15092                    child_count: children.len(),
15093                });
15094                work.extend(children.into_iter().rev().map(Work::Visit));
15095            }
15096            Work::Build { kind, child_count } => {
15097                let children = terms.split_off(terms.len() - child_count);
15098                terms.push(CppTemplateTerm::Node { kind, children });
15099            }
15100        }
15101    }
15102    terms.pop().expect("template term traversal emits one root")
15103}
15104
15105fn cpp_template_term_leaf_is_parameter<'tree>(
15106    node: Node<'tree>,
15107    text: &str,
15108    parameter_names: &[String],
15109    ancestry: &ParentIndex<'tree>,
15110) -> bool {
15111    if !parameter_names.iter().any(|parameter| parameter == text) {
15112        return false;
15113    }
15114    !ancestry.parent(node).is_some_and(|parent| {
15115        matches!(
15116            parent.kind(),
15117            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
15118        ) && parent.child_by_field_name("scope").is_some()
15119            && parent.child_by_field_name("name") == Some(node)
15120    })
15121}
15122
15123fn enclosing_cpp_declaration_node<'tree>(
15124    mut node: Node<'tree>,
15125    ancestry: &ParentIndex<'tree>,
15126) -> Option<Node<'tree>> {
15127    loop {
15128        match node.kind() {
15129            "declaration"
15130            | "function_declaration"
15131            | "field_declaration"
15132            | "function_definition" => return Some(node),
15133            _ => node = ancestry.parent(node)?,
15134        }
15135    }
15136}
15137
15138fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15139    let mut params = Vec::new();
15140    let mut cursor = parameters_node.walk();
15141    for child in parameters_node.children(&mut cursor) {
15142        match child.kind() {
15143            "explicit_object_parameter_declaration" => {
15144                if let Some(parameter) = child
15145                    .named_children(&mut child.walk())
15146                    .find(|node| node.kind() == "parameter_declaration")
15147                {
15148                    params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15149                }
15150            }
15151            "parameter_declaration" | "optional_parameter_declaration" => {
15152                params.push(cpp_parameter_type(child, source));
15153            }
15154            "variadic_parameter_declaration" => {
15155                params.push(cpp_parameter_type(child, source));
15156            }
15157            "variadic_parameter" | "..." => params.push("...".to_string()),
15158            _ => {}
15159        }
15160    }
15161
15162    if params.is_empty() {
15163        "()".to_string()
15164    } else {
15165        format!("({})", params.join(", "))
15166    }
15167}
15168
15169/// Where a C or C++ callable declaration is written, which is what decides
15170/// which modifier facts its own node is able to state.
15171enum CppCallableScope<'tree> {
15172    /// Written directly in a class, struct or union body. The declaration
15173    /// states its own storage class, and its access is the one the body's
15174    /// specifier ladder is under at that point.
15175    ClassBody {
15176        body: Node<'tree>,
15177        default_visibility: DeclaredVisibility,
15178    },
15179    /// A qualified declaration written outside every class body
15180    /// (`void Client::send(int) {}`). C++ forbids repeating `static` here and
15181    /// writes no access specifier, and whether the qualifier names a class or
15182    /// a namespace is not decidable from this file alone -- that is exactly
15183    /// what [`crate::reconcile`] needs the include-visible class table for.
15184    Qualified,
15185    /// Namespace scope or file scope, reached by an unqualified name. C has no
15186    /// member functions at all, so every C callable lands here.
15187    Unqualified,
15188}
15189
15190/// Where the declaration sits, from one upward walk of its own ancestry.
15191///
15192/// The walk stops as soon as something proves the declaration is outside every
15193/// class body: a `namespace_definition`, because a class body never encloses
15194/// one, and a `compound_statement`, because a body opens a block scope and
15195/// nothing declared in it is a member of the class the enclosing definition
15196/// belongs to. That is the same rule
15197/// `CppVisitor::visit_local_types_in_function` applies when it
15198/// drops the aggregate carrier, and it keeps this question off the O(depth)
15199/// walk a deeply nested generated header would otherwise pay per callable.
15200///
15201/// `function_declarator` is `None` for a declaration whose callable the macro
15202/// recovery reparsed out of a malformed member: there is no declarator node to
15203/// ask whether the name carries a qualifier, and a recovered member is written
15204/// in its class body anyway.
15205fn cpp_callable_scope<'tree>(
15206    declaration: Node<'tree>,
15207    function_declarator: Option<Node<'tree>>,
15208    ancestry: &ParentIndex<'tree>,
15209) -> CppCallableScope<'tree> {
15210    let mut current = Some(declaration);
15211    while let Some(node) = current {
15212        match node.kind() {
15213            "field_declaration_list" => {
15214                let default_visibility =
15215                    ancestry
15216                        .parent(node)
15217                        .map_or(DeclaredVisibility::Unknown, |class_like| {
15218                            // `struct` and `union` members default to public;
15219                            // `class` members default to private.
15220                            match class_like.kind() {
15221                                "struct_specifier" | "union_specifier" => {
15222                                    DeclaredVisibility::Public
15223                                }
15224                                _ => DeclaredVisibility::Private,
15225                            }
15226                        });
15227                return CppCallableScope::ClassBody {
15228                    body: node,
15229                    default_visibility,
15230                };
15231            }
15232            "namespace_definition" | "compound_statement" | "translation_unit" => break,
15233            _ => {}
15234        }
15235        current = ancestry.parent(node);
15236    }
15237    if cpp_callable_declarator_is_qualified(function_declarator) {
15238        CppCallableScope::Qualified
15239    } else {
15240        CppCallableScope::Unqualified
15241    }
15242}
15243
15244/// Whether the declarator's own written name carries a `::` qualifier.
15245///
15246/// A qualified declaration written outside every class body
15247/// (`long Client::send(int) {}`) names a member whose class body this file may
15248/// not contain, which is what [`cpp_callable_scope`] calls `Qualified` and the
15249/// reason [`cpp_callable_dispatch_extensibility`] refuses to answer for one.
15250fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15251    function_declarator
15252        .and_then(cpp_function_declarator_name_node)
15253        .is_some_and(|name| name.kind() == "qualified_identifier")
15254}
15255
15256/// The access one class-body member declaration is written under.
15257///
15258/// C++ states member access positionally: an `access_specifier` applies to
15259/// every member written after it until the next one, and the members before
15260/// the first one take the class keyword's default. The answer is therefore the
15261/// last specifier the body writes before the member's own list-level node,
15262/// read from the specifier's own keyword token rather than from its text. A
15263/// label a macro invocation swallowed has no specifier node or keyword token;
15264/// [`cpp_macro_swallowed_access_label`] reads it from the declarator the
15265/// grammar made of the keyword.
15266///
15267/// The body's children are read forward with one cursor rather than backwards
15268/// from the member: `Node::prev_sibling` re-descends from the root for each
15269/// step, which is the per-edge cost #2361 keeps off this walk.
15270fn cpp_member_declared_visibility<'tree>(
15271    declaration: Node<'tree>,
15272    body: Node<'tree>,
15273    default_visibility: DeclaredVisibility,
15274    source: &str,
15275    ancestry: &ParentIndex<'tree>,
15276) -> DeclaredVisibility {
15277    let mut list_level = declaration;
15278    while ancestry.parent(list_level) != Some(body) {
15279        let Some(parent) = ancestry.parent(list_level) else {
15280            return default_visibility;
15281        };
15282        list_level = parent;
15283    }
15284    let mut visibility = default_visibility;
15285    let mut cursor = body.walk();
15286    for child in body.children(&mut cursor) {
15287        if child == list_level {
15288            break;
15289        }
15290        let access = if child.kind() == "access_specifier" {
15291            child.child(0).map(|keyword| keyword.kind())
15292        } else {
15293            cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15294        };
15295        visibility = match access {
15296            Some("public") => DeclaredVisibility::Public,
15297            Some("protected") => DeclaredVisibility::Protected,
15298            Some("private") => DeclaredVisibility::Private,
15299            _ => visibility,
15300        };
15301    }
15302    visibility
15303}
15304
15305/// Whether this declaration writes the `static` storage class specifier.
15306///
15307/// What `static` *means* is the scope's business: on a class-body member it is
15308/// the receiver fact, and at file scope it is internal linkage, which is why
15309/// both [`cpp_callable_modifiers`] and [`cpp_callable_linkage`] ask the same
15310/// question and answer it differently.
15311fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15312    let mut cursor = declaration.walk();
15313    declaration.named_children(&mut cursor).any(|child| {
15314        child.kind() == "storage_class_specifier"
15315            && normalize_cpp_whitespace(node_text(child, source)) == "static"
15316    })
15317}
15318
15319/// The three modifier facts [`SignatureMetadata::with_callable_modifiers`]
15320/// publishes, as C and C++ state them.
15321struct CppCallableModifiers {
15322    is_static: bool,
15323    is_constructor: bool,
15324    visibility: DeclaredVisibility,
15325}
15326
15327/// The modifier facts a C or C++ callable declaration states in its own shape.
15328///
15329/// C++ has no `static` keyword for receivers and no visibility keyword of the
15330/// Java kind: it states both in the declaration's position. A member declared
15331/// in a class body binds the object it is selected on unless that declaration
15332/// writes the `static` storage class, and its access is the one the body's
15333/// specifier ladder is under. A function at namespace or file scope binds
15334/// nothing and has no access specifier at all, so its declared visibility is
15335/// Unknown. Linkage is recorded separately and does not declare access. C has
15336/// no member functions, and the `static` a C function writes is internal linkage,
15337/// never a receiver fact.
15338///
15339/// Recording these is what lets `receiver_contract_of` answer for a C or C++
15340/// declaration. Without them it reported no contract at all,
15341/// `modeled_procedure_key_for_unit` refused to key any C or C++ workspace
15342/// callable, no reviewed summary could name one, and every effect or taint walk
15343/// that reached one reported `callee_unkeyable` (#3493). Go closed the same gap
15344/// in #3455, Kotlin in #3453, Python in #3451.
15345///
15346/// An out-of-line member definition cannot repeat `static` or access. These
15347/// are per-file facts only: the analyzer reconciles the exact overload with
15348/// include-visible class declarations before exposing its receiver contract
15349/// (#3509). A missing declaration leaves that contract unknown.
15350fn cpp_callable_modifiers<'tree>(
15351    declaration: Node<'tree>,
15352    function_declarator: Option<Node<'tree>>,
15353    is_constructor: bool,
15354    source: &str,
15355    ancestry: &ParentIndex<'tree>,
15356) -> CppCallableModifiers {
15357    match cpp_callable_scope(declaration, function_declarator, ancestry) {
15358        CppCallableScope::ClassBody {
15359            body,
15360            default_visibility,
15361        } => CppCallableModifiers {
15362            is_static: cpp_declares_static_storage_class(declaration, source),
15363            is_constructor,
15364            visibility: cpp_member_declared_visibility(
15365                declaration,
15366                body,
15367                default_visibility,
15368                source,
15369                ancestry,
15370            ),
15371        },
15372        CppCallableScope::Qualified => CppCallableModifiers {
15373            is_static: false,
15374            is_constructor,
15375            visibility: DeclaredVisibility::Unknown,
15376        },
15377        CppCallableScope::Unqualified => CppCallableModifiers {
15378            is_static: false,
15379            is_constructor,
15380            visibility: DeclaredVisibility::Unknown,
15381        },
15382    }
15383}
15384
15385/// The persisted signature facts one C or C++ callable declaration publishes.
15386///
15387/// `linkage` is the caller's, not this function's: the node that carries the
15388/// storage class is not always the declaration this declarator sits in -- a
15389/// recovered constructor reads it from its class declaration, and a
15390/// macro-wrapped declaration reads the `static` its recovery recorded -- so the
15391/// visit site that knows which node states it passes the answer in. Recording
15392/// it here keeps it beside the declared visibility it also decides.
15393fn cpp_signature_metadata<'tree>(
15394    signature: String,
15395    function_declarator: Node<'tree>,
15396    linkage: CallableLinkage,
15397    source: &str,
15398    ancestry: &ParentIndex<'tree>,
15399) -> SignatureMetadata {
15400    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15401    let is_constructor =
15402        cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15403    let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15404        Some(declaration) => cpp_callable_modifiers(
15405            declaration,
15406            Some(function_declarator),
15407            is_constructor,
15408            source,
15409            ancestry,
15410        ),
15411        // Severe recovery left no declaration node above this declarator, so
15412        // nothing is stated here beyond what the callable's own name proves.
15413        None => CppCallableModifiers {
15414            is_static: false,
15415            is_constructor,
15416            visibility: DeclaredVisibility::Unknown,
15417        },
15418    };
15419    let enrich = |mut metadata: SignatureMetadata| {
15420        // #3507: a qualified definition repeats none of the class body's
15421        // dispatch facts, so this row publishes none either.
15422        if let Some(dispatch) = dispatch {
15423            metadata = metadata.with_dispatch_extensibility(dispatch);
15424        }
15425        metadata
15426            .with_callable_linkage(linkage)
15427            .with_callable_modifiers(
15428                modifiers.is_static,
15429                modifiers.is_constructor,
15430                modifiers.visibility,
15431            )
15432    };
15433    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15434    let return_type_identity =
15435        cpp_callable_return_type_identity(function_declarator, source, ancestry);
15436    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15437        return enrich(
15438            SignatureMetadata::new(signature, Vec::new())
15439                .with_return_type_text(return_type_text)
15440                .with_return_type_identity(return_type_identity),
15441        );
15442    };
15443    let callable_arity = cpp_callable_arity(parameters_node, source);
15444    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15445    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15446    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15447    let Some(relative_start) = signature
15448        .get(search_from..)
15449        .and_then(|suffix| suffix.find(&parameter_text))
15450    else {
15451        return enrich(
15452            SignatureMetadata::new(signature, Vec::new())
15453                .with_callable_arity(callable_arity)
15454                .with_callable_parameter_types(callable_parameter_types)
15455                .with_return_type_text(return_type_text)
15456                .with_return_type_identity(return_type_identity),
15457        );
15458    };
15459    let parameters_start = search_from + relative_start;
15460    let parameters_end = parameters_start + parameter_text.len();
15461    let mut search_start = parameters_start;
15462    let parameters = cpp_parameter_label_nodes(parameters_node)
15463        .into_iter()
15464        .filter_map(|label_node| {
15465            let label = normalize_cpp_whitespace(node_text(label_node, source));
15466            if label.is_empty() || search_start > parameters_end {
15467                return None;
15468            }
15469            let haystack = signature.get(search_start..parameters_end)?;
15470            let relative_start = haystack.find(&label)?;
15471            let start_byte = search_start + relative_start;
15472            let end_byte = start_byte + label.len();
15473            search_start = end_byte;
15474            Some(ParameterMetadata::new(label, start_byte, end_byte))
15475        })
15476        .collect();
15477    enrich(
15478        SignatureMetadata::new(signature, parameters)
15479            .with_callable_arity(callable_arity)
15480            .with_callable_parameter_types(callable_parameter_types)
15481            .with_return_type_text(return_type_text)
15482            .with_return_type_identity(return_type_identity),
15483    )
15484}
15485
15486pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15487    function_declarator: Node<'tree>,
15488    source: &str,
15489    ancestry: &ParentIndex<'tree>,
15490) -> bool {
15491    let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15492        .or_else(|| function_declarator.child_by_field_name("name"))
15493        .or_else(|| last_named_child(function_declarator))
15494    else {
15495        return false;
15496    };
15497    // An out-of-line definition writes the class in the name itself
15498    // (`Client::Client()`, `shop::Client::Client()`), so the qualifier's own
15499    // terminal component answers without any enclosing class body.
15500    if name_node.kind() == "qualified_identifier" {
15501        let owner = name_node
15502            .child_by_field_name("scope")
15503            .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15504        let member = name_node
15505            .child_by_field_name("name")
15506            .and_then(|name| canonical_cpp_qualified_component(name, source));
15507        return match (owner, member) {
15508            (Some(owner), Some(member)) => owner.name == member.name,
15509            _ => false,
15510        };
15511    }
15512    let Some(callable_name) = direct_identifier_name(name_node, source) else {
15513        return false;
15514    };
15515
15516    let mut current = ancestry.parent(function_declarator);
15517    while let Some(ancestor) = current {
15518        let owner_name = match ancestor.kind() {
15519            "class_specifier" | "struct_specifier" | "union_specifier" => {
15520                class_like_name(ancestor, source, ancestry)
15521            }
15522            "ERROR" => malformed_class_error_owner_name(ancestor, source),
15523            _ => None,
15524        };
15525        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15526            return true;
15527        }
15528        current = ancestry.parent(ancestor);
15529    }
15530    false
15531}
15532
15533/// Recover the owner name from the direct grammar shape retained when a later
15534/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
15535/// `ERROR` node:
15536///
15537/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
15538///
15539/// Direct-child checks keep this distinct from an unrelated nested class inside
15540/// a broader error region. The closing brace may be displaced past the error
15541/// node, so the opening body token is the available structural boundary.
15542fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15543    if node.kind() != "ERROR" {
15544        return None;
15545    }
15546    let keyword = node.child(0)?;
15547    if !matches!(keyword.kind(), "class" | "struct" | "union") {
15548        return None;
15549    }
15550    let name_node = node.child(1)?;
15551    let name = direct_identifier_name(name_node, source)?;
15552    let has_body = (2..node.child_count())
15553        .filter_map(|index| node.child(index))
15554        .any(|child| child.kind() == "{");
15555    has_body.then_some(name)
15556}
15557
15558/// Whether one declaration carries a native must-use attribute.
15559///
15560/// Attribute names are read only from structurally selected attribute nodes;
15561/// an identifier with the same spelling elsewhere in the declaration cannot
15562/// produce a proof. GNU's spelling is accepted for `warn_unused_result`,
15563/// while standard `nodiscard` is accepted without a namespace prefix.
15564pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15565    // The C++ grammar keeps a prefix attribute on the enclosing declaration
15566    // while the enum unit's occurrence points at its `enum_specifier`. Bring
15567    // that one structural sibling into the same declaration boundary; this
15568    // does not widen the walk to surrounding declarations.
15569    let declaration = declaration
15570        .parent()
15571        .filter(|parent| {
15572            parent.kind() == "declaration"
15573                && parent
15574                    .child_by_field_name("type")
15575                    .is_some_and(|type_node| type_node.id() == declaration.id())
15576        })
15577        .unwrap_or(declaration);
15578    let mut pending = vec![declaration];
15579    while let Some(node) = pending.pop() {
15580        if cpp_attribute_node_is_native_must_use(node, source)
15581            && cpp_attribute_is_attached_to_declaration(node, declaration)
15582        {
15583            return true;
15584        }
15585        if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15586        {
15587            continue;
15588        }
15589        push_named_children_reversed(node, &mut pending);
15590    }
15591    false
15592}
15593
15594fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15595    if node.kind() == "attribute" {
15596        return node.child_by_field_name("name").is_some_and(|name| {
15597            let name = node_text(name, source);
15598            let prefix = node
15599                .child_by_field_name("prefix")
15600                .map(|prefix| node_text(prefix, source));
15601            matches!(
15602                (prefix, name),
15603                (None, "nodiscard")
15604                    | (None, "warn_unused_result")
15605                    | (Some("gnu"), "warn_unused_result")
15606            )
15607        });
15608    }
15609    if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15610    {
15611        return false;
15612    }
15613    let Some(arguments) = node.named_child(0) else {
15614        return false;
15615    };
15616    arguments.kind() == "argument_list"
15617        && arguments.named_children(&mut arguments.walk()).any(|name| {
15618            name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15619        })
15620}
15621
15622fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15623    let mut current = attribute;
15624    while let Some(parent) = current.parent() {
15625        if parent.id() == declaration.id() {
15626            return true;
15627        }
15628        if matches!(
15629            parent.kind(),
15630            "declaration"
15631                | "field_declaration"
15632                | "function_definition"
15633                | "struct_specifier"
15634                | "class_specifier"
15635                | "union_specifier"
15636                | "enum_specifier"
15637        ) {
15638            return false;
15639        }
15640        current = parent;
15641    }
15642    false
15643}
15644
15645/// Whether export/decorator macros ahead of a callable declaration's `type`
15646/// make that type head decoration rather than a semantic return type. The
15647/// older grammar leaves the macro token in the `type` field and the real type
15648/// in an ERROR sibling; the macro-declaration grammar wraps the same token as a
15649/// leading `macro_modifier`, or the whole invocation as a
15650/// `macro_type_specifier` type. Such a declaration does not carry enough
15651/// structured evidence to persist a return type, as text or as identity.
15652fn cpp_declaration_return_type_is_macro_decorated(
15653    declaration: Node<'_>,
15654    type_node: Node<'_>,
15655    source: &str,
15656) -> bool {
15657    type_node.kind() == "macro_type_specifier"
15658        || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15659        || (cpp_export_macro_token(node_text(type_node, source))
15660            && (0..declaration.named_child_count()).any(|index| {
15661                declaration
15662                    .named_child(index)
15663                    .is_some_and(|child| child.kind() == "ERROR")
15664            }))
15665}
15666
15667/// Whether a `macro_modifier` precedes `type_node` among `declaration`'s
15668/// specifiers: the macro-declaration grammar's clean reading of an unexpanded
15669/// decorator macro ahead of the declared type (`MACRO static inline T f()`).
15670pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15671    declaration: Node<'_>,
15672    type_node: Node<'_>,
15673) -> bool {
15674    let mut head_cursor = declaration.walk();
15675    declaration
15676        .named_children(&mut head_cursor)
15677        .take_while(|child| *child != type_node)
15678        .any(|child| child.kind() == "macro_modifier")
15679}
15680
15681fn cpp_callable_return_type_parts<'tree>(
15682    function_declarator: Node<'tree>,
15683    source: &str,
15684    ancestry: &ParentIndex<'tree>,
15685) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15686    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15687        return None;
15688    }
15689    if let Some((return_type, _)) =
15690        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15691    {
15692        return Some((return_type, Vec::new()));
15693    }
15694    let mut cursor = function_declarator.walk();
15695    if let Some(trailing) = function_declarator
15696        .named_children(&mut cursor)
15697        .find(|child| child.kind() == "trailing_return_type")
15698        && let Some(type_descriptor) = trailing.named_child(0)
15699    {
15700        return Some((type_descriptor, Vec::new()));
15701    }
15702
15703    let mut current = function_declarator;
15704    let mut wrappers = Vec::new();
15705    while let Some(parent) = ancestry.parent(current) {
15706        if matches!(
15707            parent.kind(),
15708            "function_definition" | "declaration" | "field_declaration"
15709        ) {
15710            let type_node = parent.child_by_field_name("type")?;
15711            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15712                return None;
15713            }
15714            return Some((type_node, wrappers));
15715        }
15716        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15717            || (matches!(
15718                parent.kind(),
15719                "pointer_declarator"
15720                    | "reference_declarator"
15721                    | "array_declarator"
15722                    | "parenthesized_declarator"
15723            ) && parent.named_child_count() == 1
15724                && parent.named_child(0) == Some(current));
15725        if !wraps_current_declarator {
15726            return None;
15727        }
15728        match parent.kind() {
15729            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15730            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15731            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15732            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15733            _ => return None,
15734        }
15735        current = parent;
15736    }
15737    None
15738}
15739
15740/// Return the AST node that describes one callable's declared result type.
15741///
15742/// The node is returned only after the same declarator walk that establishes
15743/// the persisted C++ return identity. Consumers can therefore resolve a
15744/// nominal result or inspect its declaration attributes without parsing a
15745/// rendered signature.
15746pub fn cpp_callable_declaration_return_type_node<'tree>(
15747    callable: Node<'tree>,
15748    source: &str,
15749    ancestry: &ParentIndex<'tree>,
15750) -> Option<Node<'tree>> {
15751    let declarator = callable
15752        .child_by_field_name("declarator")
15753        .and_then(extract_function_declarator)?;
15754    cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15755}
15756
15757/// Return the structured declared result type of one callable declaration.
15758///
15759/// This accepts the enclosing declaration node so consumers do not need to
15760/// duplicate the declarator-unwrapping rules before asking for the structured
15761/// identity.
15762pub fn cpp_callable_declaration_return_type_identity<'tree>(
15763    callable: Node<'tree>,
15764    source: &str,
15765    ancestry: &ParentIndex<'tree>,
15766) -> Option<StructuredTypeIdentity> {
15767    let declarator = callable
15768        .child_by_field_name("declarator")
15769        .and_then(extract_function_declarator)?;
15770    cpp_callable_return_type_identity(declarator, source, ancestry)
15771}
15772
15773pub(crate) fn cpp_callable_return_type_identity<'tree>(
15774    function_declarator: Node<'tree>,
15775    source: &str,
15776    ancestry: &ParentIndex<'tree>,
15777) -> Option<StructuredTypeIdentity> {
15778    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15779    let (return_type, wrappers) =
15780        cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15781    cpp_apply_structured_type_wrappers(
15782        cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15783        wrappers,
15784    )
15785}
15786
15787fn cpp_structured_type_identity(
15788    node: Node<'_>,
15789    source: &str,
15790    lexical_scope: &[String],
15791) -> Option<StructuredTypeIdentity> {
15792    enum Work<'tree> {
15793        Visit(Node<'tree>),
15794        Wrap(CppStructuredTypeWrapper),
15795        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15796        BuildGeneric { argument_count: usize },
15797    }
15798
15799    let mut work = vec![Work::Visit(node)];
15800    let mut values = Vec::new();
15801    let mut builder = StructuredTypeIdentityBuilder::default();
15802    while let Some(next) = work.pop() {
15803        match next {
15804            Work::Visit(current) => match current.kind() {
15805                "type_descriptor" => {
15806                    let type_node = current
15807                        .child_by_field_name("type")
15808                        .or_else(|| current.named_child(0))?;
15809                    let mut wrappers = Vec::new();
15810                    let mut cursor = current.walk();
15811                    for child in current.named_children(&mut cursor) {
15812                        if child.id() != type_node.id() {
15813                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15814                        }
15815                    }
15816                    work.push(Work::ApplyWrappers(wrappers));
15817                    work.push(Work::Visit(type_node));
15818                }
15819                "pointer_declarator" | "abstract_pointer_declarator" => {
15820                    let child = current
15821                        .child_by_field_name("declarator")
15822                        .or_else(|| current.named_child(0))?;
15823                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15824                    work.push(Work::Visit(child));
15825                }
15826                "reference_declarator" => {
15827                    let child = current
15828                        .child_by_field_name("declarator")
15829                        .or_else(|| current.named_child(0))?;
15830                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15831                    work.push(Work::Visit(child));
15832                }
15833                "array_declarator" | "abstract_array_declarator" => {
15834                    let child = current
15835                        .child_by_field_name("declarator")
15836                        .or_else(|| current.named_child(0))?;
15837                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15838                    work.push(Work::Visit(child));
15839                }
15840                "template_type" => {
15841                    let name_node = current.child_by_field_name("name")?;
15842                    let arguments = current
15843                        .child_by_field_name("arguments")
15844                        .map(|arguments_node| {
15845                            let mut cursor = arguments_node.walk();
15846                            arguments_node
15847                                .named_children(&mut cursor)
15848                                .filter(|child| !child.is_extra() && child.kind() != "comment")
15849                                .collect::<Vec<_>>()
15850                        })
15851                        .unwrap_or_default();
15852                    work.push(Work::BuildGeneric {
15853                        argument_count: arguments.len(),
15854                    });
15855                    work.extend(arguments.into_iter().rev().map(Work::Visit));
15856                    work.push(Work::Visit(name_node));
15857                }
15858                "qualified_identifier"
15859                | "scoped_identifier"
15860                | "scoped_type_identifier"
15861                | "type_identifier"
15862                | "field_identifier"
15863                | "identifier"
15864                | "namespace_identifier"
15865                | "primitive_type" => {
15866                    values.push(builder.named(cpp_structured_named_type(
15867                        current,
15868                        source,
15869                        lexical_scope,
15870                    )?)?);
15871                }
15872                _ => {
15873                    let child = current.child_by_field_name("type").or_else(|| {
15874                        (current.named_child_count() == 1)
15875                            .then(|| current.named_child(0))
15876                            .flatten()
15877                    })?;
15878                    work.push(Work::Visit(child));
15879                }
15880            },
15881            Work::Wrap(wrapper) => {
15882                let root = values.pop()?;
15883                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15884            }
15885            Work::ApplyWrappers(wrappers) => {
15886                let mut root = values.pop()?;
15887                for wrapper in wrappers.into_iter().rev() {
15888                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15889                }
15890                values.push(root);
15891            }
15892            Work::BuildGeneric { argument_count } => {
15893                let value_count = argument_count.checked_add(1)?;
15894                let start = values.len().checked_sub(value_count)?;
15895                let mut built = values.split_off(start);
15896                let base = built.remove(0);
15897                values.push(builder.generic(base, built)?);
15898            }
15899        }
15900    }
15901    (values.len() == 1)
15902        .then(|| values.pop())
15903        .flatten()
15904        .and_then(|root| builder.finish(root))
15905}
15906
15907fn cpp_structured_named_type(
15908    node: Node<'_>,
15909    source: &str,
15910    lexical_scope: &[String],
15911) -> Option<StructuredTypeName> {
15912    let path = cpp_structured_type_path(node, source)?;
15913    let absolute = node.child_by_field_name("scope").is_none()
15914        && node.child(0).is_some_and(|child| child.kind() == "::");
15915    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15916}
15917
15918#[derive(Clone, Copy)]
15919enum CppStructuredTypeWrapper {
15920    Pointer,
15921    LvalueReference,
15922    RvalueReference,
15923    Array,
15924}
15925
15926fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15927    let mut wrappers = Vec::new();
15928    let mut current = node;
15929    loop {
15930        match current.kind() {
15931            "pointer_declarator" | "abstract_pointer_declarator" => {
15932                wrappers.push(CppStructuredTypeWrapper::Pointer)
15933            }
15934            "reference_declarator" | "abstract_reference_declarator" => {
15935                wrappers.push(cpp_reference_wrapper(current)?);
15936            }
15937            "array_declarator" | "abstract_array_declarator" => {
15938                wrappers.push(CppStructuredTypeWrapper::Array)
15939            }
15940            // An initializer and a grouping parenthesis are not type
15941            // structure. Stopping at one of them dropped every wrapper an
15942            // initialized or parenthesized declarator writes, so
15943            // `std::string &alias = value;` reported the unqualified
15944            // `std::string` while `const std::string &value` as a parameter
15945            // reported the reference it shares.
15946            "init_declarator" | "parenthesized_declarator" => {}
15947            _ => break,
15948        }
15949        let Some(child) = current
15950            .child_by_field_name("declarator")
15951            .or_else(|| current.named_child(0))
15952        else {
15953            break;
15954        };
15955        current = child;
15956    }
15957    Some(wrappers)
15958}
15959
15960fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15961    node.children(&mut node.walk())
15962        .find_map(|child| match child.kind() {
15963            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15964            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15965            _ => None,
15966        })
15967}
15968
15969fn cpp_wrap_structured_type(
15970    identity: StructuredTypeIdentity,
15971    wrapper: CppStructuredTypeWrapper,
15972) -> Option<StructuredTypeIdentity> {
15973    match wrapper {
15974        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15975        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15976        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15977        CppStructuredTypeWrapper::Array => identity.wrap_array(),
15978    }
15979}
15980
15981fn cpp_apply_structured_type_wrappers(
15982    mut identity: StructuredTypeIdentity,
15983    wrappers: Vec<CppStructuredTypeWrapper>,
15984) -> Option<StructuredTypeIdentity> {
15985    for wrapper in wrappers.into_iter().rev() {
15986        identity = cpp_wrap_structured_type(identity, wrapper)?;
15987    }
15988    Some(identity)
15989}
15990
15991fn cpp_wrap_structured_type_node(
15992    builder: &mut StructuredTypeIdentityBuilder,
15993    inner: StructuredTypeNodeId,
15994    wrapper: CppStructuredTypeWrapper,
15995) -> Option<StructuredTypeNodeId> {
15996    match wrapper {
15997        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
15998        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
15999        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
16000        CppStructuredTypeWrapper::Array => builder.array(inner),
16001    }
16002}
16003
16004/// The dot-free name components a qualified C++ name node writes, outermost
16005/// first.
16006///
16007/// The walk reads the grammar's `scope` and `name` fields rather than cutting
16008/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
16009/// shape the grammar does not name yields nothing at all.
16010pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
16011    let mut path = Vec::new();
16012    let mut stack = vec![node];
16013    while let Some(current) = stack.pop() {
16014        match current.kind() {
16015            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
16016                let component = node_text(current, source).to_string();
16017                if component.is_empty() {
16018                    return None;
16019                }
16020                path.push(component);
16021            }
16022            "template_type" | "dependent_type" => {
16023                stack.push(current.child_by_field_name("name")?);
16024            }
16025            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16026                stack.push(current.child_by_field_name("name")?);
16027                if let Some(scope) = current.child_by_field_name("scope") {
16028                    stack.push(scope);
16029                }
16030            }
16031            _ => return None,
16032        }
16033    }
16034    (!path.is_empty()).then_some(path)
16035}
16036
16037fn cpp_callable_lexical_scope<'tree>(
16038    node: Node<'tree>,
16039    source: &str,
16040    ancestry: &ParentIndex<'tree>,
16041) -> Vec<String> {
16042    let mut groups = Vec::new();
16043    let mut current = ancestry.parent(node);
16044    while let Some(parent) = current {
16045        if matches!(
16046            parent.kind(),
16047            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
16048        ) && let Some(name_node) = parent.child_by_field_name("name")
16049            && let Some(components) = cpp_structured_type_path(name_node, source)
16050            && !components.is_empty()
16051        {
16052            groups.push(components);
16053        }
16054        current = ancestry.parent(parent);
16055    }
16056    groups.reverse();
16057    groups.into_iter().flatten().collect()
16058}
16059
16060/// Whether a call on this callable's static target can select another body.
16061///
16062/// C++ says this where the member is *declared*, not where it is defined, and
16063/// that is the whole difficulty of the question: virtual-ness is inherited
16064/// (`struct Derived : Base { void run(); }` makes `Derived::run` virtual when
16065/// `Base::run` is), an out-of-line definition repeats none of it, and whether
16066/// a definition's qualifier names a class or a namespace is a class-table
16067/// question. So a row answers `Closed` only when its own node proves it:
16068///
16069/// * a written `final` is the one specifier that ends the family here, and it
16070///   is the only thing that closes a member of a class that has a base clause;
16071/// * a class-body member is `Closed` otherwise only when its class body writes
16072///   no base clause at all, because a base clause is the only evidence in this
16073///   file that some base may declare this member virtual;
16074/// * anything with a virtual-family boundary, a template head, or a
16075///   preprocessor or error region is `Open`;
16076/// * a qualified declaration outside every class body answers `None`: its
16077///   class body, which is where the fact is written, may be in another file.
16078///
16079/// The analyzer joins the `None` rows with the include-visible class-body rows
16080/// of the same logical callable (#3507), the same join #3509 makes for a
16081/// definition's declared access; a row that keeps `None` after that join stays
16082/// unknown and no dispatch proof may be built on it.
16083fn cpp_callable_dispatch_extensibility<'tree>(
16084    function_declarator: Node<'tree>,
16085    ancestry: &ParentIndex<'tree>,
16086) -> Option<DispatchExtensibility> {
16087    let mut declaration = None;
16088    // The first class body this declarator is written in. The upward walk
16089    // visits it anyway, so only the one step from the body to its class node
16090    // is extra, and only for a class-body member.
16091    let mut class_body = None;
16092    let mut current = Some(function_declarator);
16093    while let Some(node) = current {
16094        match node.kind() {
16095            "template_declaration"
16096            | "preproc_if"
16097            | "preproc_ifdef"
16098            | "preproc_else"
16099            | "preproc_elif"
16100            | "preproc_call"
16101            | "ERROR" => return Some(DispatchExtensibility::Open),
16102            "declaration" | "field_declaration" | "function_definition" => {
16103                declaration.get_or_insert(node);
16104            }
16105            "field_declaration_list" => {
16106                class_body.get_or_insert(node);
16107            }
16108            "translation_unit" => break,
16109            _ => {}
16110        }
16111        current = ancestry.parent(node);
16112    }
16113    let Some(declaration) = declaration else {
16114        return Some(DispatchExtensibility::Open);
16115    };
16116
16117    let mut saw_virtual_boundary = false;
16118    let mut stack = vec![declaration];
16119    while let Some(node) = stack.pop() {
16120        match node.kind() {
16121            "compound_statement" | "field_declaration_list" => continue,
16122            "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16123            "virtual"
16124            | "override"
16125            | "virtual_specifier"
16126            | "pure_virtual_clause"
16127            | "template_parameter_list"
16128            | "template_method"
16129            | "template_function"
16130            | "ERROR" => saw_virtual_boundary = true,
16131            _ => {}
16132        }
16133        let mut cursor = node.walk();
16134        stack.extend(node.children(&mut cursor));
16135    }
16136
16137    if saw_virtual_boundary {
16138        return Some(DispatchExtensibility::Open);
16139    }
16140    if let Some(body) = class_body {
16141        // C++ inherits virtual-ness, so a member written without a `virtual`
16142        // keyword is still virtual when a base declares it. Whether some base
16143        // does is not decidable here, so a base clause keeps the answer open.
16144        let has_base_clause = ancestry
16145            .parent(body)
16146            .and_then(cpp_class_like_base_clause)
16147            .is_some();
16148        return Some(if has_base_clause {
16149            DispatchExtensibility::Open
16150        } else {
16151            DispatchExtensibility::Closed
16152        });
16153    }
16154    if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16155        return None;
16156    }
16157    // A namespace or file scope callable is reached by name, never through a
16158    // receiver, so no other body can be selected for a call to it.
16159    Some(DispatchExtensibility::Closed)
16160}
16161
16162fn cpp_callable_linkage<'tree>(
16163    declaration: Node<'tree>,
16164    source: &str,
16165    ancestry: &ParentIndex<'tree>,
16166) -> CallableLinkage {
16167    let mut enclosed_by_class = false;
16168    let mut current = ancestry.parent(declaration);
16169    while let Some(node) = current {
16170        if node.kind() == "namespace_definition"
16171            && node
16172                .child_by_field_name("name")
16173                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16174        {
16175            return CallableLinkage::Internal;
16176        }
16177        if matches!(
16178            node.kind(),
16179            "class_specifier" | "struct_specifier" | "union_specifier"
16180        ) {
16181            if node
16182                .child_by_field_name("name")
16183                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16184            {
16185                return CallableLinkage::Internal;
16186            }
16187            enclosed_by_class = true;
16188        }
16189        // An export macro between `class` and the class name can make
16190        // tree-sitter parse the entire class as a function definition. The
16191        // structured recovery proves that this container is a named class
16192        // scope, not a local callable scope.
16193        if node.kind() == "lambda_expression"
16194            || node.kind() == "function_definition"
16195                && !is_recovered_exported_class_container(node, source)
16196        {
16197            return CallableLinkage::Internal;
16198        }
16199        current = ancestry.parent(node);
16200    }
16201
16202    if enclosed_by_class {
16203        return CallableLinkage::External;
16204    }
16205
16206    if cpp_declares_static_storage_class(declaration, source) {
16207        CallableLinkage::Internal
16208    } else {
16209        CallableLinkage::External
16210    }
16211}
16212
16213fn cpp_callable_return_type_text<'tree>(
16214    function_declarator: Node<'tree>,
16215    source: &str,
16216    ancestry: &ParentIndex<'tree>,
16217) -> Option<String> {
16218    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16219        return None;
16220    }
16221    if let Some((return_type, _)) =
16222        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16223    {
16224        let text = normalize_cpp_whitespace(node_text(return_type, source));
16225        return (!text.is_empty()).then_some(text);
16226    }
16227    let mut cursor = function_declarator.walk();
16228    if let Some(trailing) = function_declarator
16229        .named_children(&mut cursor)
16230        .find(|child| child.kind() == "trailing_return_type")
16231        && let Some(type_descriptor) = trailing.named_child(0)
16232    {
16233        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16234        if !text.is_empty() {
16235            return Some(text);
16236        }
16237    }
16238
16239    let mut current = function_declarator;
16240    let mut indirection = String::new();
16241    while let Some(parent) = ancestry.parent(current) {
16242        if matches!(
16243            parent.kind(),
16244            "function_definition" | "declaration" | "field_declaration"
16245        ) {
16246            let type_node = parent.child_by_field_name("type")?;
16247            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16248                return None;
16249            }
16250            let base = normalize_cpp_whitespace(node_text(type_node, source));
16251            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16252        }
16253        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16254            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16255                && parent.named_child_count() == 1
16256                && parent.named_child(0) == Some(current));
16257        if wraps_current_declarator {
16258            match parent.kind() {
16259                "pointer_declarator" => indirection.push('*'),
16260                "reference_declarator" => {
16261                    let reference = parent
16262                        .children(&mut parent.walk())
16263                        .find(|child| !child.is_named())
16264                        .map(|child| node_text(child, source))
16265                        .unwrap_or("&");
16266                    indirection.push_str(reference);
16267                }
16268                "init_declarator" | "parenthesized_declarator" => {}
16269                _ => return None,
16270            }
16271            current = parent;
16272            continue;
16273        }
16274        return None;
16275    }
16276    None
16277}
16278
16279pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
16280    let mut required = 0;
16281    let mut total = 0;
16282    let mut repeated = false;
16283    for slot in cpp_callable_parameter_slots(parameters_node, source) {
16284        match slot {
16285            CppParameterSlot::Declared {
16286                required: is_required,
16287                ..
16288            } => {
16289                total += 1;
16290                if is_required {
16291                    required += 1;
16292                }
16293            }
16294            CppParameterSlot::OptionalVoid => total += 1,
16295            CppParameterSlot::Ellipsis => repeated = true,
16296        }
16297    }
16298    CallableArity::new(required, total, repeated)
16299}
16300
16301/// One entry of a callable's invocation parameter list, plus an optional
16302/// unnamed `void` declaration retained for the legacy arity result.
16303///
16304/// The list excludes an explicit object parameter and a lone `void`. Every
16305/// derivation of parameter arity or type starts from this same sequence, so
16306/// those views cannot disagree about which parameters exist.
16307#[derive(Clone, Copy)]
16308enum CppParameterSlot<'tree> {
16309    Declared {
16310        parameter: Node<'tree>,
16311        required: bool,
16312    },
16313    OptionalVoid,
16314    Ellipsis,
16315}
16316
16317fn cpp_callable_parameter_slots<'tree>(
16318    parameters_node: Node<'tree>,
16319    source: &str,
16320) -> Vec<CppParameterSlot<'tree>> {
16321    let mut slots = Vec::new();
16322    let mut cursor = parameters_node.walk();
16323    for parameter in parameters_node.children(&mut cursor) {
16324        match parameter.kind() {
16325            "parameter_declaration" | "optional_parameter_declaration" => {
16326                let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16327                    && parameter
16328                        .child_by_field_name("type")
16329                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16330                if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16331                    continue;
16332                }
16333                if is_unnamed_void {
16334                    slots.push(CppParameterSlot::OptionalVoid);
16335                    continue;
16336                }
16337                slots.push(CppParameterSlot::Declared {
16338                    parameter,
16339                    required: parameter.kind() == "parameter_declaration",
16340                });
16341            }
16342            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16343                slots.push(CppParameterSlot::Ellipsis);
16344            }
16345            _ => {}
16346        }
16347    }
16348    slots
16349}
16350
16351pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16352    cpp_callable_parameter_slots(parameters_node, source)
16353        .into_iter()
16354        .filter_map(|slot| match slot {
16355            CppParameterSlot::Declared { parameter, .. } => {
16356                Some(cpp_parameter_type(parameter, source))
16357            }
16358            CppParameterSlot::Ellipsis => Some("...".to_string()),
16359            CppParameterSlot::OptionalVoid => None,
16360        })
16361        .collect()
16362}
16363
16364/// Return the written name of each callable parameter in invocation order.
16365/// C and C++ permit declarations without names; those slots deliberately stay
16366/// `None` so consumers can distinguish an unnamed formal from a guessed one.
16367pub(crate) fn cpp_callable_parameter_names(
16368    parameters_node: Node<'_>,
16369    source: &str,
16370) -> Vec<Option<String>> {
16371    cpp_callable_parameter_slots(parameters_node, source)
16372        .into_iter()
16373        .filter_map(|slot| match slot {
16374            CppParameterSlot::Declared { parameter, .. } => Some(
16375                parameter
16376                    .child_by_field_name("declarator")
16377                    .and_then(crate::structural::declarator_name_node)
16378                    .and_then(|name| direct_identifier_name(name, source)),
16379            ),
16380            CppParameterSlot::Ellipsis => Some(None),
16381            CppParameterSlot::OptionalVoid => None,
16382        })
16383        .collect()
16384}
16385
16386/// One callable parameter's parser-derived type.
16387///
16388/// A rendered spelling such as `const T&` is a source text, not a type name. A
16389/// consumer that must publish a type into a structured model - a semantic-pack
16390/// type reference, for example - reads this instead.
16391#[derive(Debug, Clone, PartialEq, Eq)]
16392pub enum CppParameterType {
16393    /// The written type reduced to a structured identity. C++ cv-qualifiers
16394    /// have no place in that model and are not represented.
16395    Structured(StructuredTypeIdentity),
16396    /// A `...` pack, which declares no parameter type at all.
16397    Ellipsis,
16398    /// A written type with no structured reduction, such as a macro-obscured,
16399    /// `decltype`-computed, or function-pointer parameter.
16400    Unstructured,
16401}
16402
16403/// The structured type of each invocation parameter, in declaration order.
16404///
16405/// The result is index-parallel with the rendered
16406/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
16407/// callable.
16408pub fn cpp_callable_parameter_type_identities<'tree>(
16409    function_declarator: Node<'tree>,
16410    source: &str,
16411    ancestry: &ParentIndex<'tree>,
16412) -> Vec<CppParameterType> {
16413    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16414        return Vec::new();
16415    };
16416    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16417    cpp_callable_parameter_slots(parameters_node, source)
16418        .into_iter()
16419        .filter_map(|slot| match slot {
16420            CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16421            CppParameterSlot::Declared { parameter, .. } => Some(
16422                cpp_parameter_type_identity(parameter, source, &lexical_scope)
16423                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16424            ),
16425            CppParameterSlot::OptionalVoid => None,
16426        })
16427        .collect()
16428}
16429
16430/// The parser-derived type of one declared object or parameter.
16431///
16432/// The declaration owns the base `type` field while the individual declarator
16433/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
16434/// lets callers distinguish several declarators in one declaration without
16435/// reparsing a rendered signature.
16436pub fn cpp_declaration_type_identity<'tree>(
16437    declaration: Node<'tree>,
16438    declarator: Node<'tree>,
16439    source: &str,
16440    ancestry: &ParentIndex<'tree>,
16441) -> Option<StructuredTypeIdentity> {
16442    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16443    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16444}
16445
16446fn cpp_parameter_type_identity(
16447    parameter: Node<'_>,
16448    source: &str,
16449    lexical_scope: &[String],
16450) -> Option<StructuredTypeIdentity> {
16451    cpp_declaration_type_identity_in_scope(
16452        parameter,
16453        cpp_parameter_declarator(parameter),
16454        source,
16455        lexical_scope,
16456    )
16457}
16458
16459fn cpp_declaration_type_identity_in_scope(
16460    declaration: Node<'_>,
16461    declarator: Option<Node<'_>>,
16462    source: &str,
16463    lexical_scope: &[String],
16464) -> Option<StructuredTypeIdentity> {
16465    let type_node = declaration.child_by_field_name("type")?;
16466    let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16467    if let Some(declarator) = declarator {
16468        return cpp_apply_structured_type_wrappers(
16469            identity,
16470            cpp_structured_declarator_wrappers(declarator)?,
16471        );
16472    }
16473    Some(identity)
16474}
16475
16476/// The comparable shape of each invocation parameter, in declaration order.
16477///
16478/// The result is index-parallel with
16479/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
16480/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
16481/// must treat as evidence of nothing rather than as agreement.
16482pub fn cpp_comparable_parameter_shapes<'tree>(
16483    function_declarator: Node<'tree>,
16484    source: &str,
16485    ancestry: &ParentIndex<'tree>,
16486) -> Vec<CppComparableSlot> {
16487    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16488        return Vec::new();
16489    };
16490    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16491    cpp_callable_parameter_slots(parameters_node, source)
16492        .into_iter()
16493        .filter_map(|slot| match slot {
16494            CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16495            CppParameterSlot::Declared { parameter, .. } => Some(
16496                cpp_comparable_parameter(parameter, source, &lexical_scope)
16497                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16498            ),
16499            CppParameterSlot::OptionalVoid => None,
16500        })
16501        .collect()
16502}
16503
16504fn cpp_comparable_parameter(
16505    parameter: Node<'_>,
16506    source: &str,
16507    lexical_scope: &[String],
16508) -> Option<CppComparableParameter> {
16509    let type_node = parameter.child_by_field_name("type")?;
16510    let levels = match cpp_parameter_declarator(parameter) {
16511        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16512        None => Vec::new(),
16513    };
16514    let mut shape = cpp_comparable_type_shape(
16515        type_node,
16516        cpp_cv_qualifiers(parameter, source),
16517        levels,
16518        source,
16519        lexical_scope,
16520    )?;
16521    shape.adjust_parameter_top_level();
16522    Some(shape)
16523}
16524
16525/// The `const` and `volatile` qualifiers written as direct named children of
16526/// `node`.
16527///
16528/// The grammar exposes `type_qualifier` as a non-field named child in exactly
16529/// the three places a parameter's qualifiers can be written: on the
16530/// `parameter_declaration` itself (the base type), on a `type_descriptor`
16531/// (inside a template argument list), and on each `pointer_declarator` level
16532/// (the pointer object). Every other qualifier the grammar admits - `restrict`
16533/// and friends - takes no part in C++ type identity, the same filter
16534/// `cpp_parameter_type` applies to the rendered spelling (#1827).
16535fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16536    let mut qualifiers = CppCvQualifiers::default();
16537    let mut cursor = node.walk();
16538    for child in node.named_children(&mut cursor) {
16539        if child.kind() != "type_qualifier" {
16540            continue;
16541        }
16542        match node_text(child, source) {
16543            "const" => qualifiers.konst = true,
16544            "volatile" => qualifiers.volatil = true,
16545            _ => {}
16546        }
16547    }
16548    qualifiers
16549}
16550
16551#[derive(Clone, Copy, Default)]
16552struct CppCvQualifiers {
16553    konst: bool,
16554    volatil: bool,
16555}
16556
16557impl CppCvQualifiers {
16558    fn union(self, other: Self) -> Self {
16559        Self {
16560            konst: self.konst || other.konst,
16561            volatil: self.volatil || other.volatil,
16562        }
16563    }
16564}
16565
16566/// One pointer, reference or array level a declarator chain adds.
16567#[derive(Clone, Copy)]
16568enum CppComparableLevel {
16569    Pointer { konst: bool, volatil: bool },
16570    Reference,
16571    Array,
16572}
16573
16574/// The levels `declarator` adds, outermost written level first.
16575///
16576/// C++ declarator syntax binds inside out: the level written closest to the
16577/// declared name is the outermost type constructor, and tree-sitter nests it
16578/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
16579/// applies in order to reach "array of pointer to int", and the qualifier of
16580/// `int * const *p` is read on the level it was written next to, the inner
16581/// pointer of the resulting type.
16582///
16583/// A declarator chain that names a function type - a function-pointer
16584/// parameter - has no comparable shape and reports `None`, matching the
16585/// structured identity channel.
16586fn cpp_comparable_declarator_levels(
16587    declarator: Node<'_>,
16588    source: &str,
16589) -> Option<Vec<CppComparableLevel>> {
16590    let mut levels = Vec::new();
16591    let mut current = declarator;
16592    loop {
16593        match current.kind() {
16594            "pointer_declarator" | "abstract_pointer_declarator" => {
16595                let qualifiers = cpp_cv_qualifiers(current, source);
16596                levels.push(CppComparableLevel::Pointer {
16597                    konst: qualifiers.konst,
16598                    volatil: qualifiers.volatil,
16599                });
16600            }
16601            "reference_declarator" | "abstract_reference_declarator" => {
16602                levels.push(CppComparableLevel::Reference);
16603            }
16604            "array_declarator" | "abstract_array_declarator" => {
16605                levels.push(CppComparableLevel::Array);
16606            }
16607            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16608            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16609            _ => return None,
16610        }
16611        let Some(next) = cpp_nested_declarator(current) else {
16612            return Some(levels);
16613        };
16614        current = next;
16615    }
16616}
16617
16618/// Reduce one written type to a comparable arena.
16619///
16620/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
16621/// two additions: each visited type node carries the cv-qualifiers written on
16622/// it, and declarator levels arrive as a prepared list rather than being
16623/// rediscovered inside the walk.
16624fn cpp_comparable_type_shape(
16625    type_node: Node<'_>,
16626    qualifiers: CppCvQualifiers,
16627    levels: Vec<CppComparableLevel>,
16628    source: &str,
16629    lexical_scope: &[String],
16630) -> Option<CppComparableParameter> {
16631    enum Work<'tree> {
16632        Visit {
16633            node: Node<'tree>,
16634            qualifiers: CppCvQualifiers,
16635        },
16636        ApplyLevels(Vec<CppComparableLevel>),
16637        BuildGeneric {
16638            argument_count: usize,
16639        },
16640    }
16641
16642    let mut nodes: Vec<CppComparableNode> = Vec::new();
16643    let mut values: Vec<usize> = Vec::new();
16644    let mut work = vec![
16645        Work::ApplyLevels(levels),
16646        Work::Visit {
16647            node: type_node,
16648            qualifiers,
16649        },
16650    ];
16651    while let Some(next) = work.pop() {
16652        match next {
16653            Work::Visit { node, qualifiers } => match node.kind() {
16654                "type_descriptor" => {
16655                    let inner_type = node
16656                        .child_by_field_name("type")
16657                        .or_else(|| node.named_child(0))?;
16658                    let mut cursor = node.walk();
16659                    let declarator = node.child_by_field_name("declarator").or_else(|| {
16660                        node.named_children(&mut cursor).find(|child| {
16661                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
16662                        })
16663                    });
16664                    let levels = match declarator {
16665                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16666                        None => Vec::new(),
16667                    };
16668                    work.push(Work::ApplyLevels(levels));
16669                    work.push(Work::Visit {
16670                        node: inner_type,
16671                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16672                    });
16673                }
16674                "sized_type_specifier" => {
16675                    // `unsigned char` is one primitive type whose components are
16676                    // partly unnamed tokens, so the whole specifier is its own
16677                    // name component. Reducing it to the `type` child would make
16678                    // `f(unsigned char)` and `f(char)` compare equal.
16679                    let name = StructuredTypeName::new(
16680                        vec![normalize_cpp_whitespace(node_text(node, source))],
16681                        lexical_scope.to_vec(),
16682                        false,
16683                    )?;
16684                    values.push(cpp_push_comparable_node(
16685                        &mut nodes,
16686                        CppComparableNode::Named {
16687                            name,
16688                            primitive: true,
16689                            konst: qualifiers.konst,
16690                            volatil: qualifiers.volatil,
16691                        },
16692                    ));
16693                }
16694                "qualified_identifier"
16695                | "scoped_identifier"
16696                | "scoped_type_identifier"
16697                | "type_identifier"
16698                | "field_identifier"
16699                | "identifier"
16700                | "namespace_identifier"
16701                | "primitive_type"
16702                | "template_type" => {
16703                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
16704                    values.push(cpp_push_comparable_node(
16705                        &mut nodes,
16706                        CppComparableNode::Named {
16707                            name,
16708                            primitive: node.kind() == "primitive_type",
16709                            konst: qualifiers.konst,
16710                            volatil: qualifiers.volatil,
16711                        },
16712                    ));
16713                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16714                        let mut cursor = arguments_node.walk();
16715                        let arguments = arguments_node
16716                            .named_children(&mut cursor)
16717                            .filter(|child| !child.is_extra() && child.kind() != "comment")
16718                            .collect::<Vec<_>>();
16719                        work.push(Work::BuildGeneric {
16720                            argument_count: arguments.len(),
16721                        });
16722                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16723                            node: argument,
16724                            qualifiers: CppCvQualifiers::default(),
16725                        }));
16726                    }
16727                }
16728                _ => {
16729                    let inner = node.child_by_field_name("type").or_else(|| {
16730                        (node.named_child_count() == 1)
16731                            .then(|| node.named_child(0))
16732                            .flatten()
16733                    })?;
16734                    work.push(Work::Visit {
16735                        node: inner,
16736                        qualifiers,
16737                    });
16738                }
16739            },
16740            Work::ApplyLevels(levels) => {
16741                let mut root = values.pop()?;
16742                for level in levels {
16743                    let node = match level {
16744                        CppComparableLevel::Pointer { konst, volatil } => {
16745                            CppComparableNode::Pointer {
16746                                inner: root,
16747                                konst,
16748                                volatil,
16749                            }
16750                        }
16751                        CppComparableLevel::Reference => {
16752                            CppComparableNode::Reference { inner: root }
16753                        }
16754                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16755                    };
16756                    root = cpp_push_comparable_node(&mut nodes, node);
16757                }
16758                values.push(root);
16759            }
16760            Work::BuildGeneric { argument_count } => {
16761                let value_count = argument_count.checked_add(1)?;
16762                let start = values.len().checked_sub(value_count)?;
16763                let mut built = values.split_off(start);
16764                let base = built.remove(0);
16765                values.push(cpp_push_comparable_node(
16766                    &mut nodes,
16767                    CppComparableNode::Generic {
16768                        base,
16769                        arguments: built,
16770                    },
16771                ));
16772            }
16773        }
16774    }
16775    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16776    debug_assert_eq!(
16777        root,
16778        nodes.len().saturating_sub(1),
16779        "comparable nodes are appended in post-order, so the root is the last one"
16780    );
16781    Some(CppComparableParameter::new(nodes, root))
16782}
16783
16784fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16785    nodes.push(node);
16786    nodes.len() - 1
16787}
16788
16789/// The template argument list of the name `node` terminates in, if any.
16790///
16791/// `std::vector<int>` writes its arguments on the `name` of a qualified
16792/// identifier, so a walk that stopped at the qualified node would reduce
16793/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
16794fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16795    let mut current = node;
16796    loop {
16797        match current.kind() {
16798            "template_type" => return current.child_by_field_name("arguments"),
16799            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16800                current = current.child_by_field_name("name")?;
16801            }
16802            _ => return None,
16803        }
16804    }
16805}
16806
16807/// The callable declarator of the declaration that covers `start_byte`.
16808///
16809/// A consumer that holds a declaration's recorded byte position rather than its
16810/// syntax node - external header extraction, for instance - uses this to reach
16811/// the same `function_declarator` the declaration walk read.
16812pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16813    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16814    loop {
16815        if matches!(
16816            current.kind(),
16817            "declaration" | "field_declaration" | "function_definition"
16818        ) && let Some(declarator) = current
16819            .child_by_field_name("declarator")
16820            .and_then(extract_function_declarator)
16821        {
16822            return Some(declarator);
16823        }
16824        current = current.parent()?;
16825    }
16826}
16827
16828fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16829    let mut labels = Vec::new();
16830    let mut cursor = parameters_node.walk();
16831    for child in parameters_node.children(&mut cursor) {
16832        match child.kind() {
16833            "parameter_declaration" | "optional_parameter_declaration" => {
16834                if let Some(name_node) = child
16835                    .child_by_field_name("declarator")
16836                    .and_then(cpp_declarator_label_node)
16837                {
16838                    labels.push(name_node);
16839                } else {
16840                    labels.push(child);
16841                }
16842            }
16843            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16844                labels.push(child);
16845            }
16846            _ => {}
16847        }
16848    }
16849    labels
16850}
16851
16852fn cpp_signature_search_start<'tree>(
16853    signature: &str,
16854    function_declarator: Node<'tree>,
16855    source: &str,
16856    ancestry: &ParentIndex<'tree>,
16857) -> usize {
16858    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16859        return 0;
16860    };
16861    let raw = node_text(enclosing, source);
16862    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16863    let offset = function_declarator
16864        .start_byte()
16865        .saturating_sub(enclosing.start_byte())
16866        .saturating_sub(leading_trim_bytes);
16867    offset.min(signature.len())
16868}
16869
16870fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16871    match node.kind() {
16872        "identifier" | "field_identifier" => Some(node),
16873        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16874            .child_by_field_name("declarator")
16875            .or_else(|| last_named_child(node))
16876            .and_then(cpp_declarator_label_node),
16877        "array_declarator" => node
16878            .child_by_field_name("declarator")
16879            .and_then(cpp_declarator_label_node),
16880        "function_declarator" => node
16881            .child_by_field_name("declarator")
16882            .or_else(|| node.child_by_field_name("name"))
16883            .or_else(|| last_named_child(node))
16884            .and_then(cpp_declarator_label_node),
16885        _ => None,
16886    }
16887}
16888
16889fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16890    let base_type = parameter
16891        .child_by_field_name("type")
16892        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16893        .unwrap_or_default();
16894    let declarator = cpp_parameter_declarator(parameter);
16895    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
16896    // are discarded, so `f(const int)` and `f(int)` declare one function. A
16897    // qualifier written next to the parameter's type is only top-level when
16898    // the declarator adds no indirection; behind a pointer, reference or array
16899    // declarator the same qualifier belongs to the pointee, referent or
16900    // element and keeps distinguishing the type (#1827).
16901    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16902    let mut cursor = parameter.walk();
16903    let qualifiers = parameter
16904        .named_children(&mut cursor)
16905        .filter(|child| child.kind() == "type_qualifier")
16906        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16907        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16908        .collect::<Vec<_>>()
16909        .join(" ");
16910    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16911        (true, _) => base_type,
16912        (_, true) => qualifiers,
16913        (false, false) => format!("{qualifiers} {base_type}"),
16914    };
16915    let declarator_suffix = declarator
16916        .map(|node| cpp_declarator_suffix_without_name(node, source))
16917        .unwrap_or_default();
16918
16919    let combined = if type_text.is_empty() {
16920        declarator_suffix
16921    } else if declarator_suffix.is_empty() {
16922        type_text
16923    } else {
16924        format!("{type_text} {declarator_suffix}")
16925    };
16926    normalize_cpp_type_text(&combined)
16927}
16928
16929fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16930    parameter.child_by_field_name("declarator").or_else(|| {
16931        // Some unnamed prototype parameters expose their abstract declarator
16932        // as a direct named child without the grammar's `declarator` field.
16933        // Recover only the structured abstract-declarator node; the parameter's
16934        // type and qualifiers are distinct children and must not be guessed from
16935        // source text.
16936        cpp_first_abstract_declarator_child(parameter)
16937    })
16938}
16939
16940/// Whether a parameter's declarator chain adds indirection - a pointer,
16941/// reference, array or function declarator - to the parameter's written type.
16942pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16943    let mut current = Some(declarator);
16944    while let Some(node) = current {
16945        if cpp_is_member_pointer_declarator(node) {
16946            return true;
16947        }
16948        if matches!(
16949            node.kind(),
16950            "pointer_declarator"
16951                | "abstract_pointer_declarator"
16952                | "reference_declarator"
16953                | "abstract_reference_declarator"
16954                | "array_declarator"
16955                | "abstract_array_declarator"
16956                | "function_declarator"
16957                | "abstract_function_declarator"
16958        ) {
16959            return true;
16960        }
16961        current = cpp_nested_declarator(node);
16962    }
16963    false
16964}
16965
16966fn is_cpp_abstract_declarator(kind: &str) -> bool {
16967    matches!(
16968        kind,
16969        "abstract_pointer_declarator"
16970            | "abstract_reference_declarator"
16971            | "abstract_array_declarator"
16972            | "abstract_function_declarator"
16973            | "abstract_parenthesized_declarator"
16974    )
16975}
16976
16977fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16978    let mut cursor = node.walk();
16979    node.named_children(&mut cursor)
16980        .find(|child| is_cpp_abstract_declarator(child.kind()))
16981}
16982
16983fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16984    node.child_by_field_name("declarator").or_else(|| {
16985        if is_cpp_abstract_declarator(node.kind()) {
16986            cpp_first_abstract_declarator_child(node)
16987        } else {
16988            // Named declarators historically use their last named child when
16989            // tree-sitter omits the field. Keep that broad fallback for
16990            // attributed, variadic, and recovered named shapes.
16991            last_named_child(node)
16992        }
16993    })
16994}
16995
16996pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
16997    matches!(
16998        node.kind(),
16999        "reference_declarator" | "abstract_reference_declarator"
17000    ) && node
17001        .children(&mut node.walk())
17002        .any(|child| child.kind() == "&&")
17003}
17004
17005/// Whether `node` is a pointer-to-member declarator, `Left::*cb` or `Left::*`.
17006/// brokk-tree-sitter-cpp 0.24.7 reads one as a `qualified_identifier` whose
17007/// `name` is a pointer declarator, possibly behind further scopes
17008/// (`A::B::*p`), with the owning class in `scope`.
17009fn cpp_is_member_pointer_declarator(node: Node<'_>) -> bool {
17010    let mut current = node;
17011    while current.kind() == "qualified_identifier" {
17012        let Some(name) = current.child_by_field_name("name") else {
17013            return false;
17014        };
17015        if matches!(
17016            name.kind(),
17017            "pointer_declarator" | "abstract_pointer_declarator" | "pointer_type_declarator"
17018        ) {
17019            return true;
17020        }
17021        current = name;
17022    }
17023    false
17024}
17025
17026fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
17027    match node.kind() {
17028        "identifier" | "field_identifier" => String::new(),
17029        // `Left::*cb` keeps its owning class, so `void (Left::*)()` and
17030        // `void (Right::*)()` stay distinct overloads (#6902 item 6).
17031        "qualified_identifier" if cpp_is_member_pointer_declarator(node) => {
17032            let scope = node
17033                .child_by_field_name("scope")
17034                .map(|scope| normalize_cpp_whitespace(node_text(scope, source)))
17035                .unwrap_or_default();
17036            let name = node
17037                .child_by_field_name("name")
17038                .map(|name| cpp_declarator_suffix_without_name(name, source))
17039                .unwrap_or_default();
17040            format!("{scope}::{name}")
17041        }
17042        "pointer_declarator" | "abstract_pointer_declarator" => {
17043            let inner = cpp_nested_declarator(node)
17044                .map(|child| cpp_declarator_suffix_without_name(child, source))
17045                .unwrap_or_default();
17046            format!("*{inner}")
17047        }
17048        "reference_declarator" | "abstract_reference_declarator" => {
17049            let inner = cpp_nested_declarator(node)
17050                .map(|child| cpp_declarator_suffix_without_name(child, source))
17051                .unwrap_or_default();
17052            let reference = if reference_declarator_is_rvalue(node) {
17053                "&&"
17054            } else {
17055                "&"
17056            };
17057            format!("{reference}{inner}")
17058        }
17059        "array_declarator" | "abstract_array_declarator" => {
17060            let inner = cpp_nested_declarator(node)
17061                .map(|child| cpp_declarator_suffix_without_name(child, source))
17062                .unwrap_or_default();
17063            let size = node
17064                .child_by_field_name("size")
17065                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17066                .unwrap_or_default();
17067            format!("{inner}[{size}]")
17068        }
17069        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
17070            let inner = cpp_nested_declarator(node);
17071            inner
17072                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
17073                .unwrap_or_default()
17074        }
17075        "function_declarator" | "abstract_function_declarator" => {
17076            let inner = cpp_nested_declarator(node)
17077                .map(|child| cpp_declarator_suffix_without_name(child, source))
17078                .unwrap_or_default();
17079            let params = node
17080                .child_by_field_name("parameters")
17081                .map(|child| cpp_parameter_signature(child, source))
17082                .unwrap_or_else(|| "()".to_string());
17083            format!("{inner}{params}")
17084        }
17085        _ => {
17086            let text = normalize_cpp_whitespace(node_text(node, source));
17087            let name = extract_declarator_name(node, source);
17088            if name.is_empty() {
17089                text
17090            } else {
17091                text.replace(&name, "").trim().to_string()
17092            }
17093        }
17094    }
17095}
17096
17097fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
17098    collapse_cpp_whitespace(
17099        suffix
17100            .trim()
17101            .trim_start_matches("->")
17102            .trim_start_matches('{')
17103            .trim_end_matches(';'),
17104    )
17105}
17106
17107pub fn normalize_cpp_whitespace(value: &str) -> String {
17108    collapse_cpp_whitespace(value)
17109}
17110
17111fn normalize_cpp_type_text(value: &str) -> String {
17112    collapse_cpp_whitespace(value)
17113        .replace(", ", ",")
17114        .replace(" <", "<")
17115        .replace("< ", "<")
17116        .replace(" >", ">")
17117}
17118
17119fn collapse_cpp_whitespace(value: &str) -> String {
17120    let mut result = String::new();
17121    let mut prev_space = false;
17122    for ch in value.chars() {
17123        if ch.is_whitespace() {
17124            if !prev_space {
17125                result.push(' ');
17126            }
17127            prev_space = true;
17128        } else {
17129            result.push(ch);
17130            prev_space = false;
17131        }
17132    }
17133    result.trim().to_string()
17134}
17135
17136fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
17137    child_by_field_or_named_child_of_kinds(
17138        node,
17139        "body",
17140        &[
17141            "declaration_list",
17142            "field_declaration_list",
17143            "enumerator_list",
17144        ],
17145    )
17146}
17147
17148/// Return a class body's actual closing brace when the parser supplied one.
17149///
17150/// A malformed namespace sentinel can leave a class node carrying unrelated
17151/// parser errors even though its own class body is complete.  `has_error()` is
17152/// therefore too coarse an admission predicate for sentinel ownership.  The
17153/// body list, however, exposes the opening and closing punctuation directly;
17154/// a real (non-missing) final `}` proves that the class did not borrow the
17155/// enclosing namespace's close.  Requiring the body to end before its parent
17156/// container also rejects a recovered node whose body swallowed that outer
17157/// boundary.
17158fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17159    if !matches!(
17160        node.kind(),
17161        "class_specifier" | "struct_specifier" | "union_specifier"
17162    ) {
17163        return None;
17164    }
17165    let body = cpp_body_node(node)?;
17166    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17167        return None;
17168    }
17169    let open = body.child(0)?;
17170    let close = body.child(body.child_count().checked_sub(1)?)?;
17171    if open.kind() != "{"
17172        || open.is_missing()
17173        || close.kind() != "}"
17174        || close.is_missing()
17175        || close.end_byte() != body.end_byte()
17176        || body.end_byte() > node.end_byte()
17177        || node
17178            .parent()
17179            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17180    {
17181        return None;
17182    }
17183    Some(close)
17184}
17185
17186fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17187    let mut stack = vec![node];
17188    while let Some(node) = stack.pop() {
17189        if node.kind() == "namespace_definition" {
17190            return true;
17191        }
17192        push_named_children_reversed(node, &mut stack);
17193    }
17194    false
17195}
17196
17197struct CppNestedNamespaceSentinel<'tree> {
17198    function: Node<'tree>,
17199    body: Node<'tree>,
17200    namespace_components: Vec<String>,
17201}
17202
17203/// Owned structural recovery metadata for a namespace-sentinel region.
17204///
17205/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
17206/// instead of the namespace/class scopes that the declaration visitor restores.
17207/// The inverted usage walk has the original CST, so it needs the same ownership
17208/// evidence without borrowing parser nodes across its file scan.  Keep this
17209/// descriptor deliberately source-range based: callers can match a reference
17210/// node by containment and then resolve its structured type spelling in the
17211/// recovered class scope.
17212#[derive(Debug, Clone)]
17213pub struct CppSentinelRecoveredOwner {
17214    pub range: Range,
17215    /// Start of the qualified owner name (`btree<P>::method`).  A leading
17216    /// return type before this byte is looked up from the namespace; parameters,
17217    /// trailing returns, and the body use the member owner scope.
17218    pub owner_name_start_byte: usize,
17219    /// Number of leading components belonging to the namespace rather than
17220    /// the qualified class owner.  A leading return type is looked up before
17221    /// every owner component, not merely before the innermost class.
17222    pub namespace_component_count: usize,
17223    pub scope_components: Vec<String>,
17224}
17225
17226#[derive(Debug, Clone)]
17227pub struct CppSentinelRecoveredClass {
17228    pub namespace_range: Range,
17229    pub namespace_scope_components: Vec<String>,
17230    pub class_range: Range,
17231    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
17232    pub scope_components: Vec<String>,
17233    /// Qualified out-of-line member definitions owned by this class.  Their
17234    /// ranges may extend beyond `class_range` when the malformed sentinel
17235    /// swallowed the namespace close and left definitions as function siblings.
17236    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17237}
17238
17239/// Resolve the lexical scope restored for a node in a malformed
17240/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
17241/// outrank class spans, which in turn outrank the surviving namespace body.
17242/// The class ancestor suffix is recovered from the original CST so nested
17243/// members keep their complete `Outer::Inner` owner chain.
17244pub fn cpp_sentinel_recovered_scope_for_node(
17245    node: Node<'_>,
17246    source: &str,
17247    recovered_classes: &[CppSentinelRecoveredClass],
17248) -> Option<Vec<String>> {
17249    let contains =
17250        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17251    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17252    for recovered in recovered_classes {
17253        for owner in recovered
17254            .owner_ranges
17255            .iter()
17256            .filter(|owner| contains(owner.range))
17257        {
17258            let replace = best_owner.is_none_or(|existing| {
17259                owner.range.end_byte.saturating_sub(owner.range.start_byte)
17260                    < existing
17261                        .range
17262                        .end_byte
17263                        .saturating_sub(existing.range.start_byte)
17264            });
17265            if replace {
17266                best_owner = Some(owner);
17267            }
17268        }
17269    }
17270    if let Some(owner) = best_owner {
17271        let mut scope = owner.scope_components.clone();
17272        if node.start_byte() < owner.owner_name_start_byte {
17273            scope.truncate(owner.namespace_component_count);
17274        }
17275        return Some(scope);
17276    }
17277
17278    let class = recovered_classes
17279        .iter()
17280        .filter(|recovered| contains(recovered.class_range))
17281        .min_by_key(|recovered| {
17282            recovered
17283                .class_range
17284                .end_byte
17285                .saturating_sub(recovered.class_range.start_byte)
17286        });
17287    let class_scope = class.is_some();
17288    let mut scope = if let Some(class) = class {
17289        class.scope_components.clone()
17290    } else {
17291        let namespace = recovered_classes
17292            .iter()
17293            .filter(|recovered| contains(recovered.namespace_range))
17294            .min_by_key(|recovered| {
17295                recovered
17296                    .namespace_range
17297                    .end_byte
17298                    .saturating_sub(recovered.namespace_range.start_byte)
17299            })?;
17300        let mut scope = namespace.namespace_scope_components.clone();
17301        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17302        let common_prefix = scope
17303            .iter()
17304            .zip(&parser_namespace)
17305            .take_while(|(recovered, parser)| recovered == parser)
17306            .count();
17307        scope.extend(parser_namespace.into_iter().skip(common_prefix));
17308        scope
17309    };
17310    if class_scope {
17311        let mut ancestor_components = Vec::new();
17312        let mut ancestor = node.parent();
17313        while let Some(current) = ancestor {
17314            if matches!(
17315                current.kind(),
17316                "class_specifier" | "struct_specifier" | "union_specifier"
17317            ) && let Some(name) = current.child_by_field_name("name")
17318                && let Some(name_components) = cpp_name_components(name, source)
17319            {
17320                ancestor_components.push(
17321                    name_components
17322                        .into_iter()
17323                        .map(|component| component.name)
17324                        .collect::<Vec<_>>(),
17325                );
17326            }
17327            ancestor = current.parent();
17328        }
17329        ancestor_components.reverse();
17330        let base_len = scope.len();
17331        for component in ancestor_components.into_iter().flatten() {
17332            if scope.len() >= base_len && scope.last() == Some(&component) {
17333                continue;
17334            }
17335            scope.push(component);
17336        }
17337    }
17338    Some(scope)
17339}
17340
17341struct CppSentinelFragmentedClassTail<'tree> {
17342    class_node: Node<'tree>,
17343    template_node: Option<Node<'tree>>,
17344    name: String,
17345    raw_supertypes: Option<Vec<String>>,
17346    fragmented: FragmentedExportBody,
17347    consumed_start: usize,
17348    /// The original node whose direct children after the class open are the
17349    /// class's members: the body of a class node, or the class-prefix `ERROR`
17350    /// itself when the class keyword, name, and `{` are its direct tokens.
17351    member_container: Node<'tree>,
17352}
17353
17354struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17355    name: String,
17356    open: Node<'tree>,
17357    raw_supertypes: Option<Vec<String>>,
17358}
17359
17360struct CppSentinelDirectBodyClassRegion {
17361    namespace_components: Vec<String>,
17362    class_start: usize,
17363    class_start_line: usize,
17364    class_close_end: usize,
17365    class_close_line: usize,
17366    name: String,
17367}
17368
17369fn cpp_sentinel_body_class_candidate<'tree>(
17370    child: Node<'tree>,
17371) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17372    if matches!(
17373        child.kind(),
17374        "class_specifier" | "struct_specifier" | "union_specifier"
17375    ) {
17376        return Some((child, None));
17377    }
17378    if child.kind() != "template_declaration" {
17379        if child.kind() == "declaration" {
17380            return Some((first_class_like_child(child)?, None));
17381        }
17382        return None;
17383    }
17384    let mut cursor = child.walk();
17385    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17386        if matches!(
17387            candidate.kind(),
17388            "class_specifier" | "struct_specifier" | "union_specifier"
17389        ) {
17390            Some(candidate)
17391        } else if candidate.kind() == "declaration" {
17392            first_class_like_child(candidate)
17393        } else {
17394            None
17395        }
17396    })?;
17397    Some((class_node, Some(child)))
17398}
17399
17400/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
17401/// namespace-sentinel body when a later member macro ends the bogus sentinel
17402/// function before the real class close. The anonymous class/open tokens and
17403/// direct identifier are the structural proof; a retained direct close would
17404/// be an ordinary malformed class rather than the fragmented tail handled here.
17405fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17406    node: Node<'tree>,
17407    source: &str,
17408) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17409    let name = malformed_class_error_owner_name(node, source)?;
17410    let mut cursor = node.walk();
17411    let children = node.children(&mut cursor).collect::<Vec<_>>();
17412    let keyword = children.first()?;
17413    let open_index = children.iter().position(|child| child.kind() == "{")?;
17414    if children[open_index + 1..]
17415        .iter()
17416        .any(|child| child.kind() == "}")
17417    {
17418        return None;
17419    }
17420    let raw_supertypes =
17421        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17422    Some(CppSentinelFragmentedClassErrorPrefix {
17423        name,
17424        open: children[open_index],
17425        raw_supertypes,
17426    })
17427}
17428
17429fn cpp_sentinel_direct_body_class_candidate<'tree>(
17430    child: Node<'tree>,
17431) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17432    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17433        return Some(candidate);
17434    }
17435    if child.kind() != "template_declaration" {
17436        return None;
17437    }
17438    let mut cursor = child.walk();
17439    let wrapper = child
17440        .named_children(&mut cursor)
17441        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17442    Some((first_class_like_child(wrapper)?, Some(child)))
17443}
17444
17445fn cpp_sentinel_direct_namespace_components(
17446    function: Node<'_>,
17447    body: Node<'_>,
17448    source: &str,
17449) -> Option<Vec<String>> {
17450    let mut cursor = function.walk();
17451    let children = function
17452        .named_children(&mut cursor)
17453        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17454        .collect::<Vec<_>>();
17455    let sentinel_index = children.iter().rposition(|child| {
17456        direct_identifier_name(*child, source)
17457            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17458    })?;
17459    let mut identifiers = Vec::new();
17460    let mut stack = children[sentinel_index + 1..]
17461        .iter()
17462        .rev()
17463        .copied()
17464        .collect::<Vec<_>>();
17465    while let Some(current) = stack.pop() {
17466        if let Some(name) = direct_identifier_name(current, source) {
17467            identifiers.push(name);
17468            continue;
17469        }
17470        let mut cursor = current.walk();
17471        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17472        stack.extend(children.into_iter().rev());
17473    }
17474    let [keyword, namespace] = identifiers.as_slice() else {
17475        return None;
17476    };
17477    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17478        .then(|| vec![namespace.clone()])
17479}
17480
17481fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17482    let mut sibling = class_semicolon.next_named_sibling();
17483    let namespace_close = loop {
17484        let Some(current) = sibling else {
17485            return false;
17486        };
17487        sibling = current.next_named_sibling();
17488        if current.kind() != "comment" {
17489            break current;
17490        }
17491    };
17492    if !cpp_is_stray_close_brace(namespace_close, source) {
17493        return false;
17494    }
17495    loop {
17496        let Some(current) = sibling else {
17497            return false;
17498        };
17499        sibling = current.next_named_sibling();
17500        if current.kind() == "comment" {
17501            continue;
17502        }
17503        return direct_identifier_name(current, source)
17504            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17505    }
17506}
17507
17508fn cpp_sentinel_macro_body_class_region<'tree>(
17509    node: Node<'tree>,
17510    source: &str,
17511    ancestry: &ParentIndex<'tree>,
17512) -> Option<CppSentinelDirectBodyClassRegion> {
17513    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17514        return None;
17515    };
17516    if node.kind() != "function_definition" || !node.has_error() {
17517        return None;
17518    }
17519    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17520    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17521    let mut cursor = body.walk();
17522    let candidates = body
17523        .named_children(&mut cursor)
17524        .filter_map(cpp_sentinel_direct_body_class_candidate)
17525        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17526        .collect::<Vec<_>>();
17527    let [(class_node, template_node)] = candidates.as_slice() else {
17528        return None;
17529    };
17530    let original_body = cpp_body_node(*class_node)?;
17531    let name = class_like_name(*class_node, source, ancestry)?;
17532    if name.is_empty() {
17533        return None;
17534    }
17535
17536    let mut sibling = node.next_named_sibling();
17537    let (class_close_start, class_close_end, class_close_line) = loop {
17538        let current = sibling?;
17539        let next = current.next_named_sibling();
17540        if cpp_is_stray_close_brace(current, source)
17541            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17542        {
17543            let semicolon = next.expect("checked above");
17544            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17545                return None;
17546            }
17547            break (
17548                current.start_byte(),
17549                semicolon.end_byte(),
17550                semicolon.end_position().row + 1,
17551            );
17552        }
17553        sibling = next;
17554    };
17555    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17556    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17557    let root = tree.root_node();
17558    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17559    // The region reparse is its own tree, so it needs its own parent index;
17560    // the caller's index answers nothing about these nodes.
17561    let reparsed_ancestry = ParentIndex::new(root);
17562    let reparsed =
17563        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17564    if reparsed.name != name
17565        || reparsed.declaration_node.start_byte() != class_node.start_byte()
17566        || reparsed.body.start_byte() != original_body.start_byte()
17567        || class_close_start <= reparsed.body.end_byte()
17568        || class_close_end <= class_node.end_byte()
17569    {
17570        return None;
17571    }
17572    Some(CppSentinelDirectBodyClassRegion {
17573        namespace_components,
17574        class_start: reparse_start,
17575        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17576            node.start_position().row + 1
17577        }),
17578        class_close_end,
17579        class_close_line,
17580        name,
17581    })
17582}
17583
17584/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
17585/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
17586///
17587/// The parser puts the namespace opener and the malformed function in one root
17588/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
17589/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
17590/// the malformed function must begin with an all-caps type, then an ERROR whose
17591/// sole identifier is `namespace`, followed by the inner namespace identifier
17592/// and a compound body; and that body must contain a complete named class or a
17593/// structurally fragmented class prefix. A text reparse cannot prove any of
17594/// those ownership boundaries.
17595fn cpp_nested_namespace_sentinel<'tree>(
17596    node: Node<'tree>,
17597    source: &str,
17598    ancestry: &ParentIndex<'tree>,
17599) -> Option<CppNestedNamespaceSentinel<'tree>> {
17600    // The shape checks below are the admission proof: a `type_identifier`
17601    // sentinel followed by an `ERROR` holding the `namespace` keyword implies
17602    // parser damage on its own, and under the macro-declaration grammar the
17603    // `macro_modifier` wrapper and the keyword in the type field are each
17604    // admitted only where the parse would otherwise contain an error.
17605    let (function, mut namespace_components) = if node.kind() == "ERROR" {
17606        let mut cursor = node.walk();
17607        let functions = node
17608            .named_children(&mut cursor)
17609            .filter(|child| child.kind() == "function_definition")
17610            .collect::<Vec<_>>();
17611        let [function] = functions.as_slice() else {
17612            return None;
17613        };
17614        let mut cursor = node.walk();
17615        let children = node.children(&mut cursor).collect::<Vec<_>>();
17616        let function_index = children
17617            .iter()
17618            .position(|child| same_node(*child, *function))?;
17619        let [outer_keyword, outer_name, outer_open] =
17620            children.get(function_index.checked_sub(3)?..function_index)?
17621        else {
17622            return None;
17623        };
17624        if outer_keyword.kind() != "namespace"
17625            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17626            || outer_open.kind() != "{"
17627        {
17628            return None;
17629        }
17630        (
17631            *function,
17632            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17633        )
17634    } else if node.kind() == "function_definition" {
17635        let declaration_list = node.parent()?;
17636        let namespace = declaration_list.parent()?;
17637        if declaration_list.kind() != "declaration_list"
17638            || namespace.kind() != "namespace_definition"
17639            || namespace.child_by_field_name("body") != Some(declaration_list)
17640        {
17641            return None;
17642        }
17643        (node, Vec::new())
17644    } else {
17645        return None;
17646    };
17647
17648    let mut cursor = function.walk();
17649    let named = function
17650        .named_children(&mut cursor)
17651        .filter(|child| child.kind() != "comment")
17652        .collect::<Vec<_>>();
17653    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17654        return None;
17655    };
17656    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
17657    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
17658    // the same token is wrapped in.
17659    let sentinel = if first_type.kind() == "type_identifier" {
17660        normalize_cpp_whitespace(node_text(*first_type, source))
17661    } else {
17662        let identifier = macro_modifier_identifier(*first_type)?;
17663        normalize_cpp_whitespace(node_text(identifier, source))
17664    };
17665    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17666        return None;
17667    }
17668    // The mis-lexed `namespace` keyword: an `ERROR` holding it under the older
17669    // grammar, or the function's bogus type field under the macro-declaration
17670    // grammar, where a keyword cannot be a type.
17671    let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17672        inner_error.named_child(0)?
17673    } else if inner_error.kind() == "type_identifier" {
17674        *inner_error
17675    } else {
17676        return None;
17677    };
17678    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17679        return None;
17680    }
17681    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17682        return None;
17683    }
17684    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17685    if inner_name.is_empty() || body.kind() != "compound_statement" {
17686        return None;
17687    }
17688    namespace_components.push(inner_name);
17689
17690    let mut cursor = body.walk();
17691    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17692        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17693            cpp_body_node(class_node).is_some()
17694                && class_like_name(class_node, source, ancestry)
17695                    .is_some_and(|name| !name.is_empty())
17696        })
17697    });
17698    if !has_complete_class
17699        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17700    {
17701        return None;
17702    }
17703
17704    Some(CppNestedNamespaceSentinel {
17705        function,
17706        body: *body,
17707        namespace_components,
17708    })
17709}
17710
17711/// Recognize a namespace-begin sentinel directly beneath the translation unit.
17712///
17713/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
17714/// function whose type is the sentinel, whose declarator is the structured
17715/// qualified name `namespace::a::b`, and whose body contains the namespace
17716/// items. Declaration indexing already reparses this bounded region. The
17717/// inverse scanner retains the original tree, so recover the same namespace
17718/// components from the declarator fields for its lexical-scope metadata.
17719fn cpp_root_namespace_sentinel<'tree>(
17720    node: Node<'tree>,
17721    source: &str,
17722    ancestry: &ParentIndex<'tree>,
17723) -> Option<CppNestedNamespaceSentinel<'tree>> {
17724    if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17725        return None;
17726    }
17727    let body = node.child_by_field_name("body")?;
17728    let declarator = node.child_by_field_name("declarator")?;
17729    let mut cursor = node.walk();
17730    let named = node
17731        .named_children(&mut cursor)
17732        .filter(|child| child.kind() != "comment")
17733        .collect::<Vec<_>>();
17734    if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17735        return None;
17736    }
17737    // The sentinel is the bogus return `type` (`type_identifier`), with the
17738    // literal `namespace` keyword leading the qualified declarator -- or,
17739    // under the macro-declaration grammar, a `macro_modifier`, with the
17740    // keyword occupying the type field (a keyword cannot be a type, which is
17741    // the same recovery evidence) and the namespace path as the declarator.
17742    let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17743        [first, second, _] => {
17744            let first_type = node.child_by_field_name("type")?;
17745            if !same_node(*first, first_type)
17746                || first_type.kind() != "type_identifier"
17747                || !same_node(*second, declarator)
17748                || !node.has_error()
17749            {
17750                return None;
17751            }
17752            (
17753                normalize_cpp_whitespace(node_text(first_type, source)),
17754                true,
17755            )
17756        }
17757        [first, second, third, _] => {
17758            let type_node = node.child_by_field_name("type")?;
17759            let identifier = macro_modifier_identifier(*first)?;
17760            if !same_node(*second, type_node)
17761                || type_node.kind() != "type_identifier"
17762                || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17763                || !same_node(*third, declarator)
17764            {
17765                return None;
17766            }
17767            (
17768                normalize_cpp_whitespace(node_text(identifier, source)),
17769                false,
17770            )
17771        }
17772        _ => return None,
17773    };
17774    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17775        return None;
17776    }
17777    if !matches!(
17778        declarator.kind(),
17779        "qualified_identifier" | "identifier" | "namespace_identifier"
17780    ) {
17781        return None;
17782    }
17783    let mut declarator_components = Vec::new();
17784    let mut valid_components = true;
17785    walk_named_tree_preorder(declarator, true, |component| {
17786        if !matches!(
17787            component.kind(),
17788            "identifier" | "namespace_identifier" | "type_identifier"
17789        ) {
17790            return WalkControl::Continue;
17791        }
17792        let Some(component) = canonical_cpp_qualified_component(component, source) else {
17793            valid_components = false;
17794            return WalkControl::Break;
17795        };
17796        declarator_components.push(component.name);
17797        WalkControl::SkipChildren
17798    });
17799    if strip_namespace_keyword {
17800        if !valid_components
17801            || declarator_components.first().map(String::as_str) != Some("namespace")
17802        {
17803            return None;
17804        }
17805        declarator_components.remove(0);
17806    }
17807    let namespace_components = declarator_components;
17808    if !valid_components
17809        || namespace_components.is_empty()
17810        || namespace_components
17811            .iter()
17812            .any(|component| component.is_empty() || cpp_export_macro_token(component))
17813    {
17814        return None;
17815    }
17816
17817    let mut cursor = body.walk();
17818    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17819        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17820            cpp_body_node(class_node).is_some()
17821                && class_like_name(class_node, source, ancestry)
17822                    .is_some_and(|name| !name.is_empty())
17823        })
17824    });
17825    if !has_complete_class
17826        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17827    {
17828        return None;
17829    }
17830
17831    Some(CppNestedNamespaceSentinel {
17832        function: node,
17833        body,
17834        namespace_components,
17835    })
17836}
17837
17838/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
17839/// malformed namespace-sentinel function.  The recovery is deliberately
17840/// structural: the class must be a direct body item, its own class node must be
17841/// erroneous and end before a unique anonymous `}` in the enclosing
17842/// declaration-list, and that namespace's next sibling must be a standalone
17843/// `;`.  The complete interior must pass the existing member-shaped reparse
17844/// gate. This avoids source brace scans and does not borrow a close from an
17845/// unrelated later declaration.
17846fn cpp_sentinel_fragmented_class_tail<'tree>(
17847    function: Node<'tree>,
17848    body: Node<'tree>,
17849    source: &str,
17850    ancestry: &ParentIndex<'tree>,
17851) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17852    let mut cursor = body.walk();
17853    let candidates = body
17854        .named_children(&mut cursor)
17855        .filter_map(|child| {
17856            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17857                let class_body = cpp_body_node(class_node)?;
17858                if !class_node.has_error() {
17859                    return None;
17860                }
17861                let name = class_like_name(class_node, source, ancestry)?;
17862                let raw_supertypes =
17863                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17864                        .then(|| extract_cpp_supertypes(class_node, source));
17865                return Some((
17866                    class_node,
17867                    template_node,
17868                    name,
17869                    class_body,
17870                    class_body.start_byte().checked_add(1)?,
17871                    raw_supertypes,
17872                    class_body,
17873                ));
17874            }
17875            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17876            Some((
17877                child,
17878                None,
17879                prefix.name,
17880                prefix.open,
17881                prefix.open.end_byte(),
17882                prefix.raw_supertypes,
17883                child,
17884            ))
17885        })
17886        .collect::<Vec<_>>();
17887    let [
17888        (
17889            class_node,
17890            template_node,
17891            name,
17892            class_body,
17893            reparse_start,
17894            raw_supertypes,
17895            member_container,
17896        ),
17897    ] = candidates.as_slice()
17898    else {
17899        return None;
17900    };
17901    if name.is_empty() {
17902        return None;
17903    }
17904
17905    let (close, semicolon) =
17906        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17907
17908    let reparse_end = close.start_byte();
17909    if *reparse_start >= reparse_end {
17910        return None;
17911    }
17912    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17913    if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17914        return None;
17915    }
17916    let class_range = Range {
17917        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17918        end_byte: semicolon.end_byte(),
17919        start_line: template_node.map_or(class_node.start_position().row, |node| {
17920            node.start_position().row
17921        }) + 1,
17922        end_line: semicolon.end_position().row + 1,
17923    };
17924    Some(CppSentinelFragmentedClassTail {
17925        class_node: *class_node,
17926        template_node: *template_node,
17927        name: name.clone(),
17928        raw_supertypes: raw_supertypes.clone(),
17929        fragmented: FragmentedExportBody {
17930            reparse_start: *reparse_start,
17931            reparse_end,
17932            class_range,
17933        },
17934        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17935        member_container: *member_container,
17936    })
17937}
17938
17939/// The alias declarations a fragmented sentinel class still holds as
17940/// structured direct members in the original tree (#6902).
17941///
17942/// The member reparse cannot keep them: after an access label it reads
17943/// `using value_type = int;` in statement context, as a `using` variable
17944/// named `value_type`, and loses the target. The original node keeps the
17945/// alias kind, the target type, the exact source range, and the template and
17946/// preprocessor context. Ownership is proven by position alone: a direct
17947/// child of the class's member container, inside the interior the member
17948/// reparse covers, or inside a preprocessor conditional that is one. An alias
17949/// nested anywhere else is not admitted here.
17950fn cpp_sentinel_fragmented_class_aliases<'tree>(
17951    tail: &CppSentinelFragmentedClassTail<'tree>,
17952) -> Vec<Node<'tree>> {
17953    let mut aliases = Vec::new();
17954    let mut containers = vec![tail.member_container];
17955    while let Some(container) = containers.pop() {
17956        let mut cursor = container.walk();
17957        for child in container.named_children(&mut cursor) {
17958            if child.start_byte() < tail.fragmented.reparse_start
17959                || child.end_byte() > tail.fragmented.reparse_end
17960            {
17961                continue;
17962            }
17963            match child.kind() {
17964                // A conditional member keeps its owner; its guard comes from
17965                // this same original ancestry.
17966                "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
17967                | "preproc_elifdef" => containers.push(child),
17968                "alias_declaration" if !child.has_error() => aliases.push(child),
17969                "template_declaration"
17970                    if !child.has_error()
17971                        && child
17972                            .named_children(&mut child.walk())
17973                            .any(|declaration| declaration.kind() == "alias_declaration") =>
17974                {
17975                    aliases.push(child)
17976                }
17977                _ => {}
17978            }
17979        }
17980    }
17981    aliases
17982}
17983
17984/// Recover the class and out-of-line owner scopes from every malformed
17985/// namespace-sentinel region in `root`.
17986///
17987/// This is the shared structural counterpart to
17988/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
17989/// reuses the visitor's sentinel/class admission predicates instead of parsing
17990/// source text a second time.  The returned values own only ranges and names, so
17991/// they can be retained by an inverted usage scan after the tree borrow ends.
17992pub fn cpp_sentinel_recovered_classes(
17993    root: Node<'_>,
17994    source: &str,
17995) -> Vec<CppSentinelRecoveredClass> {
17996    // The macro-declaration grammar absorbs the sentinel damage into structured
17997    // nodes and can leave the whole tree clean: `macro_modifier` and its kin are
17998    // admitted only where the older grammar would have reported an error, so a
17999    // clean tree carrying them holds the same recovery evidence. A tree with
18000    // neither errors nor macro-declaration nodes has no sentinel region.
18001    if !root.has_error()
18002        && !subtree_contains(root, |node| {
18003            matches!(
18004                node.kind(),
18005                "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
18006            )
18007        })
18008    {
18009        return Vec::new();
18010    }
18011    // This scan owns its walk of `root`, so it owns the parent index that walk
18012    // asks its ancestor questions through. Built after the error gate: a clean
18013    // tree without macro-declaration evidence returns without paying for one.
18014    let ancestry = ParentIndex::new(root);
18015    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
18016    let mut stack = vec![root];
18017    while let Some(current) = stack.pop() {
18018        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
18019            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
18020        {
18021            let namespace_components = cpp_sentinel_recovered_namespace_components(
18022                recovered.function,
18023                &recovered.namespace_components,
18024                source,
18025            );
18026            let fragmented = cpp_sentinel_fragmented_class_tail(
18027                recovered.function,
18028                recovered.body,
18029                source,
18030                &ancestry,
18031            );
18032            let mut class_candidates = Vec::new();
18033            let mut cursor = recovered.body.walk();
18034            for (class_node, template_node) in recovered
18035                .body
18036                .named_children(&mut cursor)
18037                .filter_map(cpp_sentinel_body_class_candidate)
18038            {
18039                let Some(name) = class_like_name(class_node, source, &ancestry) else {
18040                    continue;
18041                };
18042                if name.is_empty() {
18043                    continue;
18044                }
18045                let is_fragmented = fragmented
18046                    .as_ref()
18047                    .is_some_and(|tail| same_node(tail.class_node, class_node));
18048                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
18049                    continue;
18050                }
18051                let class_range = if is_fragmented {
18052                    fragmented
18053                        .as_ref()
18054                        .map(|tail| tail.fragmented.class_range)
18055                        .expect("fragmented class range is present when class matches")
18056                } else {
18057                    cpp_declaration_range(template_node.unwrap_or(class_node))
18058                };
18059                class_candidates.push((class_range, name));
18060            }
18061            if let Some(fragmented) = fragmented
18062                .as_ref()
18063                .filter(|tail| tail.class_node.kind() == "ERROR")
18064            {
18065                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
18066            }
18067
18068            let mut owner_ranges =
18069                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
18070            cpp_sentinel_extend_unique_owner_ranges(
18071                &mut owner_ranges,
18072                cpp_sentinel_recovered_sibling_owner_ranges(
18073                    recovered.function,
18074                    &namespace_components,
18075                    source,
18076                ),
18077            );
18078            for (class_range, name) in class_candidates {
18079                push_cpp_sentinel_recovered_class(
18080                    &mut recovered_classes,
18081                    cpp_declaration_range(recovered.body),
18082                    &namespace_components,
18083                    class_range,
18084                    name,
18085                    &owner_ranges,
18086                );
18087            }
18088
18089            if let Some(declaration_list) = recovered
18090                .function
18091                .parent()
18092                .filter(|parent| parent.kind() == "declaration_list")
18093            {
18094                let outer_namespace =
18095                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
18096                push_cpp_sentinel_sibling_classes(
18097                    &mut recovered_classes,
18098                    declaration_list,
18099                    recovered.function,
18100                    &outer_namespace,
18101                    source,
18102                    &ancestry,
18103                );
18104            }
18105        } else if let Some(region) =
18106            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
18107        {
18108            let namespace_components = cpp_sentinel_recovered_namespace_components(
18109                current,
18110                &region.namespace_components,
18111                source,
18112            );
18113            let owner_container = current
18114                .parent()
18115                .filter(|parent| parent.kind() == "declaration_list")
18116                .unwrap_or(current);
18117            let owner_ranges =
18118                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18119            push_cpp_sentinel_recovered_class(
18120                &mut recovered_classes,
18121                cpp_declaration_range(owner_container),
18122                &namespace_components,
18123                Range {
18124                    start_byte: region.class_start,
18125                    end_byte: region.class_close_end,
18126                    start_line: region.class_start_line,
18127                    end_line: region.class_close_line,
18128                },
18129                region.name,
18130                &owner_ranges,
18131            );
18132        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
18133            // A generic sentinel-prefixed class can be reduced as a malformed
18134            // function/ERROR without the explicit `namespace X` token pair.
18135            // Reuse the declaration visitor's bounded reparse and retain only
18136            // the recovered class identity/range here.
18137            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
18138                region;
18139            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18140                continue;
18141            };
18142            let root = tree.root_node();
18143            let template_node = cpp_sentinel_reparsed_leading_template(root);
18144            // A region reparse is its own tree and needs its own parent index.
18145            let reparsed_ancestry = ParentIndex::new(root);
18146            let Some(reparsed_class) =
18147                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
18148            else {
18149                continue;
18150            };
18151            let class_node = reparsed_class.declaration_node;
18152            let name = reparsed_class.name;
18153            let namespace_components =
18154                cpp_sentinel_recovered_namespace_components(current, &[], source);
18155            let owner_container = current
18156                .parent()
18157                .filter(|parent| parent.kind() == "declaration_list")
18158                .unwrap_or(current);
18159            let mut owner_ranges =
18160                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18161            cpp_sentinel_extend_unique_owner_ranges(
18162                &mut owner_ranges,
18163                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18164            );
18165            push_cpp_sentinel_recovered_class(
18166                &mut recovered_classes,
18167                cpp_declaration_range(owner_container),
18168                &namespace_components,
18169                Range {
18170                    start_byte: class_start,
18171                    end_byte: close_end,
18172                    start_line: class_node.start_position().row + 1,
18173                    end_line: class_node.end_position().row + 1,
18174                },
18175                name,
18176                &owner_ranges,
18177            );
18178            if owner_container.kind() == "declaration_list" {
18179                push_cpp_sentinel_sibling_classes(
18180                    &mut recovered_classes,
18181                    owner_container,
18182                    current,
18183                    &namespace_components,
18184                    source,
18185                    &ancestry,
18186                );
18187            }
18188        }
18189
18190        let mut cursor = current.walk();
18191        stack.extend(current.named_children(&mut cursor));
18192    }
18193    // A shallower sentinel can expose nested classes as apparent namespace
18194    // siblings even after a deeper sentinel proves that a containing class
18195    // owns their ranges. Drop those shadow descriptors; scope recovery starts
18196    // from the proven containing class and appends parser-visible class
18197    // ancestors, preserving the full `Outer::Inner` chain.
18198    let shadowed = recovered_classes
18199        .iter()
18200        .map(|candidate| {
18201            recovered_classes.iter().any(|container| {
18202                container.class_range.start_byte <= candidate.class_range.start_byte
18203                    && container.class_range.end_byte >= candidate.class_range.end_byte
18204                    && container.class_range != candidate.class_range
18205                    && container.namespace_scope_components.len()
18206                        > candidate.namespace_scope_components.len()
18207                    && container
18208                        .namespace_scope_components
18209                        .starts_with(&candidate.namespace_scope_components)
18210            })
18211        })
18212        .collect::<Vec<_>>();
18213    let mut index = 0usize;
18214    recovered_classes.retain(|_| {
18215        let keep = !shadowed[index];
18216        index += 1;
18217        keep
18218    });
18219    recovered_classes
18220}
18221
18222/// A flat sentinel can swallow the first class while leaving later classes and
18223/// their out-of-line definitions as ordinary declaration-list siblings.  Once
18224/// the malformed class proves the sentinel envelope, retain those structurally
18225/// complete sibling classes under the same surviving namespace so every member
18226/// owner in the region uses one recovery contract.
18227fn push_cpp_sentinel_sibling_classes<'tree>(
18228    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18229    declaration_list: Node<'tree>,
18230    sentinel_node: Node<'tree>,
18231    namespace_components: &[String],
18232    source: &str,
18233    ancestry: &ParentIndex<'tree>,
18234) {
18235    let owner_ranges =
18236        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18237    let namespace_range = cpp_declaration_range(declaration_list);
18238    let mut cursor = declaration_list.walk();
18239    for (class_node, template_node) in declaration_list
18240        .named_children(&mut cursor)
18241        .filter(|child| !same_node(*child, sentinel_node))
18242        .filter_map(cpp_sentinel_body_class_candidate)
18243    {
18244        let Some(name) = class_like_name(class_node, source, ancestry) else {
18245            continue;
18246        };
18247        if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18248            continue;
18249        }
18250        push_cpp_sentinel_recovered_class(
18251            recovered_classes,
18252            namespace_range,
18253            namespace_components,
18254            cpp_declaration_range(template_node.unwrap_or(class_node)),
18255            name,
18256            &owner_ranges,
18257        );
18258    }
18259}
18260
18261fn push_cpp_sentinel_recovered_class(
18262    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18263    namespace_range: Range,
18264    namespace_components: &[String],
18265    class_range: Range,
18266    name: String,
18267    owner_ranges: &[CppSentinelRecoveredOwner],
18268) {
18269    let mut scope_components = namespace_components.to_vec();
18270    scope_components.push(name);
18271    let owner_ranges = owner_ranges
18272        .iter()
18273        .filter(|owner| owner.scope_components.starts_with(&scope_components))
18274        .cloned()
18275        .collect::<Vec<_>>();
18276    if recovered_classes.iter().any(|existing| {
18277        existing.class_range == class_range && existing.scope_components == scope_components
18278    }) {
18279        return;
18280    }
18281    recovered_classes.push(CppSentinelRecoveredClass {
18282        namespace_range,
18283        namespace_scope_components: namespace_components.to_vec(),
18284        class_range,
18285        scope_components,
18286        owner_ranges,
18287    });
18288}
18289
18290fn cpp_sentinel_recovered_namespace_components(
18291    function: Node<'_>,
18292    recovered_components: &[String],
18293    source: &str,
18294) -> Vec<String> {
18295    let mut ancestor_components = Vec::new();
18296    let mut ancestor = function.parent();
18297    while let Some(current) = ancestor {
18298        if current.kind() == "namespace_definition"
18299            && let Some(name_node) = current.child_by_field_name("name")
18300            && let Some(components) = cpp_name_components(name_node, source)
18301        {
18302            ancestor_components.push(
18303                components
18304                    .into_iter()
18305                    .map(|component| component.name)
18306                    .collect::<Vec<_>>(),
18307            );
18308        }
18309        ancestor = current.parent();
18310    }
18311    ancestor_components.reverse();
18312    let mut ancestors = ancestor_components
18313        .into_iter()
18314        .flatten()
18315        .collect::<Vec<_>>();
18316
18317    let overlap = (0..=ancestors.len().min(recovered_components.len()))
18318        .rev()
18319        .find(|length| {
18320            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18321        })
18322        .unwrap_or(0);
18323    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18324    ancestors
18325}
18326
18327fn cpp_sentinel_recovered_owner_ranges(
18328    body: Node<'_>,
18329    namespace_components: &[String],
18330    source: &str,
18331) -> Vec<CppSentinelRecoveredOwner> {
18332    let mut owners = Vec::new();
18333    walk_named_tree_preorder(body, true, |node| {
18334        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18335    });
18336    owners
18337}
18338
18339fn cpp_sentinel_collect_owner_range(
18340    node: Node<'_>,
18341    namespace_components: &[String],
18342    source: &str,
18343    owners: &mut Vec<CppSentinelRecoveredOwner>,
18344) -> WalkControl {
18345    if node.kind() != "function_definition" {
18346        return WalkControl::Continue;
18347    }
18348    let Some(function_declarator) = extract_function_declarator(node) else {
18349        return WalkControl::Continue;
18350    };
18351    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18352        return WalkControl::Continue;
18353    };
18354    let Some(mut components) = cpp_name_components(name_node, source) else {
18355        return WalkControl::Continue;
18356    };
18357    if components.len() <= 1 {
18358        return WalkControl::Continue;
18359    }
18360    components.pop();
18361    let mut owner_components = components
18362        .into_iter()
18363        .map(|component| component.name)
18364        .collect::<Vec<_>>();
18365    let overlap = (0..=namespace_components.len().min(owner_components.len()))
18366        .rev()
18367        .find(|length| {
18368            owner_components[..*length]
18369                == namespace_components[namespace_components.len().saturating_sub(*length)..]
18370        })
18371        .unwrap_or(0);
18372    let mut scope_components = namespace_components.to_vec();
18373    scope_components.extend(owner_components.drain(overlap..));
18374    if scope_components.len() <= namespace_components.len() {
18375        return WalkControl::Continue;
18376    }
18377    let range = cpp_declaration_range(node);
18378    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18379        existing.range == range && existing.scope_components == scope_components
18380    }) {
18381        owners.push(CppSentinelRecoveredOwner {
18382            range,
18383            owner_name_start_byte: name_node.start_byte(),
18384            namespace_component_count: namespace_components.len(),
18385            scope_components,
18386        });
18387    }
18388    WalkControl::Continue
18389}
18390
18391fn cpp_sentinel_extend_unique_owner_ranges(
18392    owners: &mut Vec<CppSentinelRecoveredOwner>,
18393    additional: Vec<CppSentinelRecoveredOwner>,
18394) {
18395    for owner in additional {
18396        if !owners.iter().any(|existing| {
18397            existing.range == owner.range && existing.scope_components == owner.scope_components
18398        }) {
18399            owners.push(owner);
18400        }
18401    }
18402}
18403
18404fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18405    if node.kind() != "ERROR" || node.named_child_count() != 1 {
18406        return false;
18407    }
18408    let Some(end_name) = node.named_child(0) else {
18409        return false;
18410    };
18411    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18412        return false;
18413    }
18414    let mut cursor = node.walk();
18415    node.children(&mut cursor)
18416        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18417}
18418
18419/// Collect owner definitions that the malformed sentinel left as later
18420/// declaration-list siblings. Parser-visible namespace siblings are a hard
18421/// boundary: their declarations must keep their own lexical namespace.
18422fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18423    parent: Node<'_>,
18424    sentinel_node: Node<'_>,
18425    namespace_components: &[String],
18426    source: &str,
18427) -> Vec<CppSentinelRecoveredOwner> {
18428    let mut owners = Vec::new();
18429    let mut after_sentinel = false;
18430    let mut cursor = parent.walk();
18431    for child in parent.named_children(&mut cursor) {
18432        if !after_sentinel {
18433            if same_node(child, sentinel_node) {
18434                after_sentinel = true;
18435            }
18436            continue;
18437        }
18438        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18439    }
18440    owners
18441}
18442
18443/// Collect owner definitions after a malformed namespace, stopping only at
18444/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
18445/// enclosing container is not trusted to belong to the recovered namespace.
18446fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18447    parent: Node<'_>,
18448    sentinel_node: Node<'_>,
18449    namespace_components: &[String],
18450    source: &str,
18451) -> Option<Vec<CppSentinelRecoveredOwner>> {
18452    let mut owners = Vec::new();
18453    let mut after_namespace = false;
18454    let mut cursor = parent.walk();
18455    for child in parent.named_children(&mut cursor) {
18456        if !after_namespace {
18457            if same_node(child, sentinel_node) {
18458                after_namespace = true;
18459            }
18460            continue;
18461        }
18462        if cpp_sentinel_namespace_end(child, source) {
18463            return Some(owners);
18464        }
18465        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18466    }
18467    None
18468}
18469
18470fn cpp_sentinel_append_owner_ranges(
18471    sibling: Node<'_>,
18472    namespace_components: &[String],
18473    source: &str,
18474    owners: &mut Vec<CppSentinelRecoveredOwner>,
18475) {
18476    walk_named_tree_preorder(sibling, true, |node| {
18477        if node.kind() == "namespace_definition" {
18478            return WalkControl::SkipChildren;
18479        }
18480        cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18481    });
18482}
18483
18484fn cpp_sentinel_recovered_sibling_owner_ranges(
18485    sentinel_node: Node<'_>,
18486    namespace_components: &[String],
18487    source: &str,
18488) -> Vec<CppSentinelRecoveredOwner> {
18489    let Some(declaration_list) = sentinel_node
18490        .parent()
18491        .filter(|parent| parent.kind() == "declaration_list")
18492    else {
18493        return Vec::new();
18494    };
18495    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18496        declaration_list,
18497        sentinel_node,
18498        namespace_components,
18499        source,
18500    );
18501
18502    let Some(namespace) = declaration_list
18503        .parent()
18504        .filter(|parent| parent.kind() == "namespace_definition")
18505    else {
18506        return owners;
18507    };
18508    let Some(outer_parent) = namespace.parent() else {
18509        return owners;
18510    };
18511    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18512        outer_parent,
18513        namespace,
18514        namespace_components,
18515        source,
18516    ) {
18517        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18518    }
18519    owners
18520}
18521
18522fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18523    let mut current = function_declarator.child_by_field_name("declarator")?;
18524    loop {
18525        if let Some(name) = macro_decorated_unqualified_name(current) {
18526            current = name;
18527            continue;
18528        }
18529        if matches!(
18530            current.kind(),
18531            "qualified_identifier"
18532                | "scoped_identifier"
18533                | "scoped_type_identifier"
18534                | "identifier"
18535                | "field_identifier"
18536                | "operator_name"
18537                | "destructor_name"
18538                | "literal_operator_name"
18539        ) {
18540            return Some(current);
18541        }
18542        current = current
18543            .child_by_field_name("declarator")
18544            .or_else(|| current.child_by_field_name("name"))
18545            .or_else(|| last_named_child(current))?;
18546    }
18547}
18548
18549/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
18550/// `::` between the halves. The scope is an attribute-like macro token, not
18551/// a namespace; `Name` is the declared name.
18552///
18553/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
18554/// tree-sitter joins the attribute macro and the constructor name into one
18555/// `qualified_identifier` and marks the separator it had to invent as MISSING.
18556/// That flag is the grammar's own record that no separator is in the source,
18557/// so read it instead of looking for `::` in the node text. A genuine
18558/// `Outer::Inner` keeps a present separator and is declined here, and so is
18559/// the explicit-global `::name`, whose `::` is present and whose scope is
18560/// absent.
18561fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18562    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18563        return None;
18564    }
18565    let mut cursor = node.walk();
18566    if node
18567        .children(&mut cursor)
18568        .any(|child| child.kind() == "::" && !child.is_missing())
18569    {
18570        return None;
18571    }
18572    node.child_by_field_name("name")
18573}
18574
18575fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18576    enum Component<'tree> {
18577        Node(Node<'tree>),
18578        Name(Node<'tree>),
18579    }
18580    let mut stack = vec![Component::Node(node)];
18581    let mut components = Vec::new();
18582    while let Some(component) = stack.pop() {
18583        let node = match component {
18584            Component::Name(node) => {
18585                components.push(canonical_cpp_qualified_component(
18586                    node.child_by_field_name("name")?,
18587                    source,
18588                )?);
18589                continue;
18590            }
18591            Component::Node(node) => node,
18592        };
18593        if let Some(name) = macro_decorated_unqualified_name(node) {
18594            stack.push(Component::Node(name));
18595            continue;
18596        }
18597        match node.kind() {
18598            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18599                stack.push(Component::Name(node));
18600                if let Some(scope) = node.child_by_field_name("scope") {
18601                    stack.push(Component::Node(scope));
18602                }
18603            }
18604            _ => components.push(canonical_cpp_qualified_component(node, source)?),
18605        }
18606    }
18607    Some(components)
18608}
18609
18610fn cpp_sentinel_fragment_boundary<'tree>(
18611    function: Node<'tree>,
18612    class_node: Node<'tree>,
18613    class_body: Node<'tree>,
18614    source: &str,
18615) -> Option<(Node<'tree>, Node<'tree>)> {
18616    let declaration_list = function.parent()?;
18617    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18618        return None;
18619    }
18620    let namespace = declaration_list.parent()?;
18621    if namespace.kind() != "namespace_definition"
18622        || namespace.child_by_field_name("body") != Some(declaration_list)
18623    {
18624        return None;
18625    }
18626    let mut cursor = declaration_list.walk();
18627    let closes = declaration_list
18628        .children(&mut cursor)
18629        .filter(|child| {
18630            !child.is_named()
18631                && child.kind() == "}"
18632                && child.start_byte() >= function.end_byte()
18633                && child.start_byte() > class_node.end_byte()
18634                && child.start_byte() > class_body.start_byte()
18635        })
18636        .collect::<Vec<_>>();
18637    let [close] = closes.as_slice() else {
18638        return None;
18639    };
18640    let semicolon = namespace.next_named_sibling()?;
18641    if !cpp_is_stray_semicolon(semicolon, source)
18642        || close.end_byte() != namespace.end_byte()
18643        || semicolon.start_byte() < namespace.end_byte()
18644    {
18645        return None;
18646    }
18647    Some((*close, semicolon))
18648}
18649
18650/// Detect the bogus declaration/function tree that tree-sitter recovers for a
18651/// region prefixed by an object-like macro sentinel the parser cannot see
18652/// (issue #941), and return the byte range `[start, end)` of the swallowed
18653/// declaration interior to reparse.
18654///
18655/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
18656/// `function_definition` whose first non-comment named child is the sentinel
18657/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
18658/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
18659/// the real items.
18660/// `start` is the end of the sentinel identifier -- everything after it is the
18661/// genuine source. `end` is the node's end, extended across any trailing empty
18662/// `;` statement the mis-parse displaced past the node (the class/struct closing
18663/// semicolon), so the reparse sees a complete, brace-balanced item.
18664///
18665/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
18666/// node (`has_error`), or -- under the macro-declaration grammar, which parses
18667/// the sentinel region without recovery -- carry the sentinel as a
18668/// `macro_modifier`, a production admitted only where the parse would
18669/// otherwise contain an error. Unknown annotation/export macros can make a
18670/// real callable error-recovered even though tree-sitter still preserves its
18671/// declarator, so a preserved callable is admitted only when a displaced class
18672/// keyword precedes that declarator. The clean-reparse-to-items gate in
18673/// `cpp_reparsed_items_are_indexable` is the final arbiter.
18674/// Return the reparse start and, when present, the structurally recovered class
18675/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
18676/// separately from the reparse start because an opaque template-declaration
18677/// macro may precede it.
18678fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18679    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18680        return None;
18681    }
18682    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
18683    // retain a valid function declarator despite the unknown export macro making
18684    // the outer node erroneous. Remember that declarator for the ordering gate
18685    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
18686    // class keyword precedes a spurious callable assembled from a later member.
18687    let mut declarator_cursor = node.walk();
18688    let preserved_callable = node
18689        .children_by_field_name("declarator", &mut declarator_cursor)
18690        .find_map(extract_function_declarator);
18691    // Leading documentation comments are attached to the malformed
18692    // `function_definition` as named children.  They are not part of the
18693    // sentinel prefix, so select the first non-comment child structurally
18694    // rather than requiring the sentinel to be child zero.  This is the shape
18695    // emitted for nlohmann/json's `basic_json`: its class documentation comment
18696    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
18697    // envelope otherwise ends at the first nested union.
18698    let mut cursor = node.walk();
18699    let first = node
18700        .named_children(&mut cursor)
18701        .find(|child| child.kind() != "comment")?;
18702    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
18703    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
18704    // the same token is wrapped in. The wrapper is itself the recovery
18705    // evidence the older grammar's `has_error` carried, so a clean envelope is
18706    // admitted only in that form. The macro-declaration grammar can also keep
18707    // the sentinel as the `type` and absorb the displaced head that follows it
18708    // (`BEGIN_NS TEMPLATE_DECLARATION class Box { ... }`) into a
18709    // `macro_modifier_declarator` chain, which carries the same evidence.
18710    let (first_end, sentinel) = if first.kind() == "type_identifier" {
18711        if !node.has_error()
18712            && node
18713                .child_by_field_name("declarator")
18714                .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18715        {
18716            return None;
18717        }
18718        (
18719            first.end_byte(),
18720            normalize_cpp_whitespace(node_text(first, source)),
18721        )
18722    } else {
18723        let identifier = macro_modifier_identifier(first)?;
18724        (
18725            first.end_byte(),
18726            normalize_cpp_whitespace(node_text(identifier, source)),
18727        )
18728    };
18729    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18730        return None;
18731    }
18732    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
18733    // makes the trailing sentinel of one region and the leading sentinel of the
18734    // next both land as bare macro-token identifiers ahead of the real content.
18735    // Advance past every leading macro-token identifier so the reparse begins at
18736    // genuine source rather than another sentinel that would re-form the bogus
18737    // shape and fail the reparse gate.
18738    let mut start = first_end;
18739    let mut after_first = false;
18740    let mut cursor = node.walk();
18741    for child in node.named_children(&mut cursor) {
18742        if !after_first {
18743            if same_node(child, first) {
18744                after_first = true;
18745            }
18746            continue;
18747        }
18748        let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18749            Some(child)
18750        } else {
18751            macro_modifier_identifier(child)
18752        };
18753        if macro_token.is_some_and(|token| {
18754            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18755        }) {
18756            start = child.end_byte();
18757        } else {
18758            break;
18759        }
18760    }
18761    // An additional opaque template-declaration macro before a class can be
18762    // folded into the bogus function's qualified declarator.  In that shape
18763    // the macro is not a direct sibling we can skip above; tree-sitter exposes
18764    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
18765    // Reparse from that keyword (or a real preceding `template` keyword) so the
18766    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
18767    // a class nested in a genuine sentinel-wrapped namespace lies after the
18768    // body opening and must not change the established region start.
18769    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18770    let mut class_start = None;
18771    let mut template_start = None;
18772    let mut stack = vec![node];
18773    while let Some(current) = stack.pop() {
18774        if current.start_byte() >= prefix_end {
18775            continue;
18776        }
18777        if matches!(
18778            current.kind(),
18779            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18780        ) {
18781            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18782                "class" | "struct" | "union" | "enum" => {
18783                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18784                        seen.min(current.start_byte())
18785                    }));
18786                }
18787                "template" => {
18788                    template_start =
18789                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
18790                            seen.min(current.start_byte())
18791                        }));
18792                }
18793                _ => {}
18794            }
18795        }
18796        let mut cursor = current.walk();
18797        stack.extend(current.children(&mut cursor));
18798    }
18799    if preserved_callable.is_some_and(|callable| {
18800        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18801    }) {
18802        return None;
18803    }
18804    if let Some(class_start) = class_start {
18805        start = template_start
18806            .filter(|template_start| *template_start < class_start)
18807            .unwrap_or(class_start);
18808    }
18809    Some((start, class_start))
18810}
18811
18812/// Locate a sentinel-prefixed class whose malformed declaration was split across
18813/// root-level siblings. The true class close is represented structurally as a
18814/// lone `}` error followed by the class's displaced `;`; nested method/body
18815/// errors are not direct siblings of the sentinel node and therefore cannot
18816/// satisfy this pair.
18817fn cpp_sentinel_macro_class_region<'tree>(
18818    node: Node<'tree>,
18819    source: &str,
18820) -> Option<(usize, usize, usize, usize, usize, usize)> {
18821    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18822        return None;
18823    };
18824    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18825        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18826        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18827    if class_start >= body_open_start {
18828        return None;
18829    }
18830    let sibling_close = {
18831        let mut sibling = node.next_named_sibling();
18832        let mut found = None;
18833        while let Some(current) = sibling {
18834            let next = current.next_named_sibling();
18835            if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18836                found = Some((
18837                    current.start_byte(),
18838                    semicolon.end_byte(),
18839                    semicolon.end_position().row + 1,
18840                ));
18841                break;
18842            }
18843            sibling = next;
18844        }
18845        found
18846    };
18847    // A stray `};` sibling is this class's close only when the bounded reparse
18848    // agrees the first body-bearing class ENDS there. When the malformed
18849    // envelope swallowed the class's true close, the scan can promote a much
18850    // later scope's close instead -- in protobuf-generated headers
18851    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
18852    // `struct TableStruct_*` paired with the first message class's `};`, making
18853    // the recovered "class body" span whole `namespace {}` blocks and minting
18854    // namespace-scope classes as nested members of the recovered class, which
18855    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
18856    // (#2275). On disagreement, fall through to the suffix-reparse boundary
18857    // below, which derives the close from the class node's own balanced body
18858    // range.
18859    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18860        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18861            return false;
18862        };
18863        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18864        // A region reparse is its own tree and needs its own parent index.
18865        let reparsed_ancestry = ParentIndex::new(tree.root_node());
18866        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18867            tree.root_node(),
18868            template_node,
18869            source,
18870            &reparsed_ancestry,
18871        ) else {
18872            return false;
18873        };
18874        let body = reparsed_class.body;
18875        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18876    });
18877    let (class_close_start, class_close_end, class_close_line) =
18878        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18879            (class_close_start, class_close_end, class_close_line)
18880        } else {
18881            // When the malformed envelope itself is an ERROR, tree-sitter can
18882            // leave the class's balanced close in the source while promoting
18883            // all following members to siblings. Reparse the complete suffix
18884            // and use the first body-bearing class node's own field range as
18885            // the partition boundary. This keeps balancing in tree-sitter and
18886            // preserves the source's original byte offsets.
18887            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18888            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18889            // A region reparse is its own tree and needs its own parent index.
18890            let reparsed_ancestry = ParentIndex::new(tree.root_node());
18891            let reparsed_class = cpp_sentinel_reparsed_class(
18892                tree.root_node(),
18893                template_node,
18894                source,
18895                &reparsed_ancestry,
18896            )?;
18897            let body = reparsed_class.body;
18898            let class_close_end = body.end_byte();
18899            let class_close_start = class_close_end.checked_sub(1)?;
18900            let class_close_line = body.end_position().row + 1;
18901            (class_close_start, class_close_end, class_close_line)
18902        };
18903    if class_close_start <= class_start {
18904        return None;
18905    }
18906
18907    // Reparse only far enough to expose the class body opening. This is a
18908    // structured check that the candidate really begins with a body-bearing
18909    // class-like item; the original malformed tree cannot provide that node.
18910    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18911    let class_root = tree.root_node();
18912    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18913    // A region reparse is its own tree and needs its own parent index.
18914    let reparsed_ancestry = ParentIndex::new(class_root);
18915    let reparsed_class =
18916        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18917    let body = reparsed_class.body;
18918    // The class body opening must agree with the malformed wrapper's structured
18919    // body field. This rejects an inner nested class while permitting later
18920    // members to remain fragmented as root-level siblings in the bounded parse.
18921    if body.start_byte() != body_open_start {
18922        return None;
18923    }
18924    let body_start = body.start_byte().checked_add(1)?;
18925    (body_start < class_close_start).then_some((
18926        reparse_start,
18927        class_start,
18928        body_start,
18929        class_close_start,
18930        class_close_end,
18931        class_close_line,
18932    ))
18933}
18934
18935/// Find the `{` token immediately following the class/struct/union/enum token
18936/// at `class_start` in the malformed tree. The token is anonymous in the C++
18937/// grammar, so this deliberately walks all children (not only named children)
18938/// and relies on sibling structure rather than source-text searching.
18939fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18940    let mut stack = vec![node];
18941    while let Some(current) = stack.pop() {
18942        if current.start_byte() == class_start
18943            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18944        {
18945            // The macro-declaration grammar keeps the class whole: the token's
18946            // specifier ancestor owns the body, and its `{` is the body's
18947            // first child rather than a later sibling of the token.
18948            let mut ancestor = current.parent();
18949            while let Some(parent) = ancestor {
18950                if matches!(
18951                    parent.kind(),
18952                    "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18953                ) {
18954                    if let Some(body) = cpp_body_node(parent) {
18955                        return body
18956                            .child(0)
18957                            .filter(|open| open.kind() == "{")
18958                            .map(|open| open.start_byte());
18959                    }
18960                    break;
18961                }
18962                if matches!(parent.kind(), "ERROR" | "declaration") {
18963                    break;
18964                }
18965                ancestor = parent.parent();
18966            }
18967            let mut sibling = current.next_sibling();
18968            while let Some(candidate) = sibling {
18969                if candidate.kind() == "{" {
18970                    return Some(candidate.start_byte());
18971                }
18972                sibling = candidate.next_sibling();
18973            }
18974        }
18975        let mut cursor = current.walk();
18976        stack.extend(current.children(&mut cursor));
18977    }
18978    None
18979}
18980
18981/// The class body that tree-sitter displaced out of a sentinel-prefixed
18982/// declaration and left as the malformed node's next sibling.
18983///
18984/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
18985/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
18986/// the last child of that `ERROR` and its `{` opens a sibling
18987/// `compound_statement` instead. The body is still the malformed tree's own
18988/// structured token, which is what the caller's `body.start_byte() !=
18989/// body_open_start` agreement check needs; it just is not reachable by walking
18990/// forward from the class token inside the node.
18991fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
18992    node.next_named_sibling()
18993        .filter(|sibling| sibling.kind() == "compound_statement")
18994}
18995
18996fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
18997    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
18998    let mut end = if class_start.is_some() {
18999        cpp_macro_prefixed_class_end(source, start)?
19000    } else {
19001        node.end_byte()
19002    };
19003    if class_start.is_none()
19004        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
19005    {
19006        end = end.max(namespace_end);
19007    }
19008    let mut sibling = node.next_named_sibling();
19009    while let Some(current) = sibling {
19010        if !cpp_is_stray_semicolon(current, source) {
19011            break;
19012        }
19013        end = current.end_byte();
19014        sibling = current.next_named_sibling();
19015    }
19016    (start < end).then_some((start, end))
19017}
19018
19019/// Extend a sentinel reparse through a following namespace that tree-sitter
19020/// flattened into the sentinel node's sibling list.
19021///
19022/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
19023/// unknown macro becomes a false function return type and consumes the first
19024/// namespace body. A second `namespace detail` then loses its enclosing node:
19025/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
19026/// attaches its declarations to the surrounding error tree. Reparse from that
19027/// structured keyword so tree-sitter, rather than a source-text brace scan,
19028/// supplies the complete namespace boundary.
19029fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
19030    let mut sibling = node.next_sibling();
19031    let keyword = loop {
19032        let candidate = sibling?;
19033        sibling = candidate.next_sibling();
19034        if candidate.kind() != "comment" {
19035            break candidate;
19036        }
19037    };
19038    if keyword.kind() != "namespace" {
19039        return None;
19040    }
19041    let name = loop {
19042        let candidate = sibling?;
19043        sibling = candidate.next_sibling();
19044        if candidate.kind() != "comment" {
19045            break candidate;
19046        }
19047    };
19048    if cpp_namespace_name_components(name, source).is_empty() {
19049        return None;
19050    }
19051    let open = loop {
19052        let candidate = sibling?;
19053        sibling = candidate.next_sibling();
19054        if candidate.kind() != "comment" {
19055            break candidate;
19056        }
19057    };
19058    if open.kind() != "{" {
19059        return None;
19060    }
19061
19062    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
19063    let root = tree.root_node();
19064    let mut cursor = root.walk();
19065    let namespace = root
19066        .named_children(&mut cursor)
19067        .find(|candidate| candidate.kind() != "comment")?;
19068    (namespace.kind() == "namespace_definition"
19069        && namespace.start_byte() == keyword.start_byte()
19070        && namespace.child_by_field_name("body").is_some())
19071    .then_some(namespace.end_byte())
19072}
19073
19074/// Parse the source suffix beginning at a structurally recovered class/template
19075/// keyword and return the end of its first body-bearing class item.  The parser,
19076/// rather than a brace scanner, owns nested-body balancing.  This is needed when
19077/// the original error tree truncates the class and scatters later members as
19078/// top-level siblings.
19079fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
19080    let tree = cpp_reparse_region_items(source, start, source.len())?;
19081    let root = tree.root_node();
19082    let mut cursor = root.walk();
19083    for item in root.named_children(&mut cursor) {
19084        if item.end_byte() <= start || item.kind() == "comment" {
19085            continue;
19086        }
19087        let mut stack = vec![item];
19088        while let Some(current) = stack.pop() {
19089            if matches!(
19090                current.kind(),
19091                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19092            ) && cpp_body_node(current).is_some()
19093            {
19094                return Some(current.end_byte());
19095            }
19096            let mut cursor = current.walk();
19097            stack.extend(current.named_children(&mut cursor));
19098        }
19099        // The recovered prefix is required to begin with the class item.  If
19100        // the first real item is something else, fail closed rather than skip
19101        // arbitrary source looking for a later class.
19102        return None;
19103    }
19104    None
19105}
19106
19107/// An empty `;` statement: the displaced closing semicolon of a struct/class that
19108/// the sentinel mis-parse split off past the bogus function node.
19109fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
19110    node.kind() == "expression_statement"
19111        && node.named_child_count() == 0
19112        && node_text(node, source).trim() == ";"
19113}
19114
19115fn cpp_stray_close_followed_by_semicolon<'tree>(
19116    close: Node<'tree>,
19117    next: Option<Node<'tree>>,
19118    source: &str,
19119) -> Option<Node<'tree>> {
19120    cpp_is_stray_close_brace(close, source)
19121        .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
19122        .flatten()
19123}
19124
19125/// Recover the member that follows CPython's object header macro when
19126/// tree-sitter folds both declarations into one malformed field.
19127///
19128/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
19129/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
19130/// sole child is the real field declarator. The tail may wrap that identifier
19131/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
19132/// malformed wrappers do not provide the same evidence that this is the
19133/// CPython macro boundary.
19134#[derive(Clone, Copy)]
19135pub(crate) struct RecoveredPyObjectHeadField<'tree> {
19136    pub(crate) type_node: Node<'tree>,
19137    pub(crate) name: Node<'tree>,
19138    pub(crate) declarator: Node<'tree>,
19139}
19140
19141impl RecoveredPyObjectHeadField<'_> {
19142    pub(crate) fn pointer_depth(self) -> i32 {
19143        let mut depth = 0;
19144        let mut current = self.declarator;
19145        while current != self.name {
19146            debug_assert_eq!(current.kind(), "pointer_declarator");
19147            depth += 1;
19148            current = current
19149                .child_by_field_name("declarator")
19150                .expect("recovered PyObject field pointer has an inner declarator");
19151        }
19152        depth
19153    }
19154}
19155
19156pub(crate) fn recovered_pyobject_head_field<'tree>(
19157    node: Node<'tree>,
19158    source: &str,
19159) -> Option<RecoveredPyObjectHeadField<'tree>> {
19160    if node.kind() != "field_declaration" {
19161        return None;
19162    }
19163    let type_node = node.child_by_field_name("type")?;
19164    if type_node.kind() != "type_identifier"
19165        || node_text(type_node, source).trim() != "PyObject_HEAD"
19166    {
19167        return None;
19168    }
19169    let pseudo_declarator = node.child_by_field_name("declarator")?;
19170    let mut cursor = node.walk();
19171    let errors = node
19172        .named_children(&mut cursor)
19173        .filter(|child| child.kind() == "ERROR")
19174        .collect::<Vec<_>>();
19175    let [error] = errors.as_slice() else {
19176        return None;
19177    };
19178    if error.named_child_count() != 1 {
19179        return None;
19180    }
19181    let error_child = error.named_child(0)?;
19182    if pseudo_declarator.kind() == "field_identifier"
19183        && error.start_byte() >= pseudo_declarator.end_byte()
19184        && error_child.kind() == "identifier"
19185    {
19186        return Some(RecoveredPyObjectHeadField {
19187            type_node: pseudo_declarator,
19188            name: error_child,
19189            declarator: error_child,
19190        });
19191    }
19192    if pseudo_declarator.kind() != "pointer_declarator"
19193        || error.end_byte() > pseudo_declarator.start_byte()
19194        || error_child.kind() != "identifier"
19195    {
19196        return None;
19197    }
19198    let mut name = pseudo_declarator;
19199    while name.kind() == "pointer_declarator" {
19200        name = name.child_by_field_name("declarator")?;
19201    }
19202    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19203        type_node: error_child,
19204        name,
19205        declarator: pseudo_declarator,
19206    })
19207}
19208
19209/// Recover the real field name when a leading object-like annotation macro
19210/// displaces a qualified type into tree-sitter's bit-field recovery shape.
19211///
19212/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
19213/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
19214/// `bitfield_clause` containing an error plus an assignment.  The assignment's
19215/// left field is the only structured declaration name in that malformed tail.
19216/// A real bit-field is excluded by the all-caps macro type and required error.
19217fn recovered_macro_qualified_field_clause<'tree>(
19218    node: Node<'tree>,
19219    source: &str,
19220) -> Option<Node<'tree>> {
19221    if node.kind() != "field_declaration" {
19222        return None;
19223    }
19224    let macro_type = node.child_by_field_name("type")?;
19225    if macro_type.kind() != "type_identifier"
19226        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19227    {
19228        return None;
19229    }
19230    let mut cursor = node.walk();
19231    node.named_children(&mut cursor)
19232        .find(|child| child.kind() == "bitfield_clause")
19233}
19234
19235fn recovered_macro_qualified_field_declarators<'tree>(
19236    node: Node<'tree>,
19237    source: &str,
19238) -> Option<Vec<Node<'tree>>> {
19239    let pseudo_declarator = node.child_by_field_name("declarator")?;
19240    if pseudo_declarator.kind() != "field_identifier" {
19241        return None;
19242    }
19243    let clause = recovered_macro_qualified_field_clause(node, source)?;
19244    if !(0..clause.named_child_count()).any(|index| {
19245        clause
19246            .named_child(index)
19247            .is_some_and(|child| child.kind() == "ERROR")
19248    }) {
19249        return None;
19250    }
19251    let mut recovered = Vec::new();
19252    let mut stack = vec![clause];
19253    while let Some(current) = stack.pop() {
19254        if current.kind() == "assignment_expression"
19255            && let Some(left) = current.child_by_field_name("left")
19256            && extract_variable_name(left, source).is_some()
19257        {
19258            recovered.push(left);
19259            break;
19260        }
19261        let mut cursor = current.walk();
19262        stack.extend(current.named_children(&mut cursor));
19263    }
19264    if recovered.is_empty() {
19265        return None;
19266    }
19267    let mut cursor = node.walk();
19268    recovered.extend(
19269        node.children_by_field_name("declarator", &mut cursor)
19270            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19271    );
19272    Some(recovered)
19273}
19274
19275/// Recover a macro-qualified constructor that tree-sitter represents as one
19276/// field declaration. The constructor call remains inside the direct recovery
19277/// error, while each member initializer becomes a false function declarator.
19278/// The class owner proves the constructor name and lets the caller ignore those
19279/// initializer declarators.
19280fn recovered_macro_qualified_constructor_call<'tree>(
19281    node: Node<'tree>,
19282    class_name: &str,
19283    source: &str,
19284) -> Option<Node<'tree>> {
19285    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19286    let error = bitfield
19287        .named_child(0)
19288        .filter(|child| child.kind() == "ERROR")?;
19289    let mut stack = vec![error];
19290    while let Some(current) = stack.pop() {
19291        if current.kind() == "call_expression"
19292            && current
19293                .child_by_field_name("function")
19294                .is_some_and(|function| node_text(function, source) == class_name)
19295            && current
19296                .child_by_field_name("arguments")
19297                .is_some_and(|arguments| arguments.kind() == "argument_list")
19298        {
19299            return Some(current);
19300        }
19301        let mut cursor = current.walk();
19302        stack.extend(current.named_children(&mut cursor));
19303    }
19304    None
19305}
19306
19307/// The constructor head of a macro-decorated constructor that the
19308/// macro-declaration grammar reads as one field declaration
19309/// (`MSC_WARNING(suppress : 26495) CONSTEXPR Buffer(grow_fun grow) noexcept
19310/// : size_(size), grow_(grow) {}`, #1838). The macro is the field's `type`,
19311/// the constructor head is its first declarator (possibly inside a
19312/// `macro_modifier_declarator`), the first member initializer is a
19313/// `bitfield_clause` call, every later initializer is another function
19314/// declarator, and the body is the `default_value` initializer list. A
19315/// constructor declares no type, and no real field pairs a callable declarator
19316/// with a bitfield, so a head naming the enclosing class beside a bitfield
19317/// call is this shape. The caller publishes the head and none of the
19318/// initializer declarators.
19319fn recovered_macro_initialized_constructor_declarator<'tree>(
19320    node: Node<'tree>,
19321    class_name: &str,
19322    source: &str,
19323) -> Option<Node<'tree>> {
19324    if node.kind() != "field_declaration" {
19325        return None;
19326    }
19327    let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19328    if cpp_function_declarator_name_node(constructor)
19329        .is_none_or(|name| node_text(name, source) != class_name)
19330    {
19331        return None;
19332    }
19333    let mut cursor = node.walk();
19334    let initializer = node
19335        .named_children(&mut cursor)
19336        .find(|child| child.kind() == "bitfield_clause")?
19337        .named_child(0)?;
19338    (initializer.kind() == "call_expression").then_some(constructor)
19339}
19340
19341/// Recover a macro-qualified member function declaration that tree-sitter
19342/// represents as a pseudo-field. An object-like export macro before a qualified
19343/// return type can displace the namespace and type into an ERROR/bitfield
19344/// recovery, leaving the callable as a structured `call_expression`.
19345///
19346/// The caller must route this shape before ordinary declarator classification;
19347/// otherwise the displaced namespace identifier is published as a field.
19348fn recovered_macro_qualified_function_call<'tree>(
19349    node: Node<'tree>,
19350    source: &str,
19351) -> Option<Node<'tree>> {
19352    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19353    let declarator = node.child_by_field_name("declarator")?;
19354    if declarator.kind() != "field_identifier" {
19355        return None;
19356    }
19357    let mut cursor = node.walk();
19358    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19359    if !named.iter().any(|child| {
19360        child.kind() == "storage_class_specifier"
19361            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19362    }) {
19363        return None;
19364    }
19365    let mut bitfield_cursor = bitfield.walk();
19366    let payload = bitfield
19367        .named_children(&mut bitfield_cursor)
19368        .collect::<Vec<_>>();
19369    let [displaced_error, call] = payload.as_slice() else {
19370        return None;
19371    };
19372    if displaced_error.kind() != "ERROR"
19373        || displaced_error.named_child_count() != 1
19374        || displaced_error
19375            .named_child(0)
19376            .is_none_or(|child| child.kind() != "identifier")
19377        || call.kind() != "call_expression"
19378        || call
19379            .child_by_field_name("function")
19380            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19381        || call
19382            .child_by_field_name("arguments")
19383            .is_none_or(|arguments| arguments.kind() != "argument_list")
19384    {
19385        return None;
19386    }
19387    Some(*call)
19388}
19389
19390/// The real constructor declarator of a macro-decorated constructor the
19391/// macro-declaration grammar reads as an explicit specifier, an `ERROR`
19392/// holding the macro and the constructor head as a `macro_modifier_declarator`,
19393/// and the member initializer mis-read as the function's own declarator
19394/// (`explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}`, #2552 shape 1).
19395/// The constructor head inside the `ERROR` is what names the class; the
19396/// declarator the `function_definition` fields is the initializer, never the
19397/// member.
19398fn recovered_macro_decorated_constructor_declarator<'tree>(
19399    node: Node<'tree>,
19400    class_name: &str,
19401    source: &str,
19402) -> Option<Node<'tree>> {
19403    if node.kind() != "function_definition" {
19404        return None;
19405    }
19406    let mut cursor = node.walk();
19407    let mut heads = Vec::new();
19408    for error in node
19409        .named_children(&mut cursor)
19410        .filter(|child| child.kind() == "ERROR")
19411    {
19412        let mut stack = vec![error];
19413        while let Some(current) = stack.pop() {
19414            let is_constructor_head = match current.kind() {
19415                "macro_modifier_declarator" | "function_declarator" => {
19416                    extract_function_declarator(current).is_some_and(|declarator| {
19417                        cpp_function_declarator_name_node(declarator)
19418                            .is_some_and(|name| node_text(name, source) == class_name)
19419                    })
19420                }
19421                _ => false,
19422            };
19423            if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19424                heads.push(declarator);
19425                continue;
19426            }
19427            let mut cursor = current.walk();
19428            stack.extend(current.named_children(&mut cursor));
19429        }
19430    }
19431    let [head] = heads.as_slice() else {
19432        return None;
19433    };
19434    Some(*head)
19435}
19436
19437fn recovered_macro_qualified_function_parameters(
19438    arguments: Node<'_>,
19439    source: &str,
19440) -> Option<(String, Vec<String>)> {
19441    if arguments.kind() != "argument_list" {
19442        return None;
19443    }
19444    let mut cursor = arguments.walk();
19445    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19446    if named.is_empty() {
19447        return Some(("()".to_string(), Vec::new()));
19448    }
19449    let mut types = Vec::new();
19450    let mut labels = Vec::new();
19451    let mut index = 0;
19452    while index < named.len() {
19453        let parameter_type = named[index];
19454        let parameter_name = named.get(index + 1).copied()?;
19455        if !matches!(
19456            parameter_type.kind(),
19457            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19458        ) || parameter_name.kind() != "ERROR"
19459            || parameter_name.named_child_count() != 1
19460            || parameter_name
19461                .named_child(0)
19462                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19463        {
19464            return None;
19465        }
19466        let parameter_name = parameter_name.named_child(0)?;
19467        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19468        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19469        index += 2;
19470    }
19471    Some((format!("({})", types.join(", ")), labels))
19472}
19473
19474/// Recognize the phantom field tree-sitter emits for a macro-qualified
19475/// function return type.  For example,
19476/// `static API result_type ThresholdForSmallA() { ... }` can become a
19477/// `field_declaration` (`API` as the type and `result_type` as a field name)
19478/// followed by a clean `function_definition` for `ThresholdForSmallA`.
19479///
19480/// Keep this predicate entirely tied to the CST envelope: the type must be an
19481/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
19482/// the declaration must carry a missing semicolon rather than a real one, and
19483/// the immediate named sibling must expose a function declarator.  A real
19484/// macro-decorated field with an explicit semicolon therefore remains a field.
19485pub fn recovered_macro_return_type_node<'tree>(
19486    node: Node<'tree>,
19487    source: &str,
19488) -> Option<Node<'tree>> {
19489    if node.kind() != "field_declaration" {
19490        return None;
19491    }
19492    let macro_type = node.child_by_field_name("type")?;
19493    if macro_type.kind() != "type_identifier"
19494        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19495    {
19496        return None;
19497    }
19498    let declarator = node.child_by_field_name("declarator")?;
19499    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19500        return None;
19501    }
19502    let mut has_missing_semicolon = false;
19503    let mut has_real_semicolon = false;
19504    for child in children_iter(node) {
19505        if child.kind() != ";" {
19506            continue;
19507        }
19508        if child.is_missing() {
19509            has_missing_semicolon = true;
19510        } else {
19511            has_real_semicolon = true;
19512        }
19513    }
19514    if !has_missing_semicolon || has_real_semicolon {
19515        return None;
19516    }
19517    let mut next = node.next_named_sibling();
19518    while next.is_some_and(|sibling| sibling.kind() == "comment") {
19519        next = next.and_then(|sibling| sibling.next_named_sibling());
19520    }
19521    let next = next?;
19522    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19523        return None;
19524    }
19525    let function_declarator = next.child_by_field_name("declarator")?;
19526    extract_function_declarator(function_declarator).map(|_| declarator)
19527}
19528
19529/// The omitted-semicolon class field the macro-declaration grammar folds into
19530/// the head of the normally typed function that follows it. The older grammar
19531/// left `DWORD preserved_field` ahead of `void PreserveFollowingFunction() {}`
19532/// as a damaged `field_declaration`; the macro-declaration grammar reads the
19533/// same pair as two leading `macro_modifier`s of the clean function
19534/// definition. Only an exact modifier pair ahead of a real type field and a
19535/// function declarator is a field: a lone modifier ahead of the type is the
19536/// macro-return shape (`static API result_type f() { ... }`), which carries
19537/// no field.
19538fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19539    if node.kind() != "function_definition" {
19540        return None;
19541    }
19542    let type_node = node.child_by_field_name("type")?;
19543    let declarator = node.child_by_field_name("declarator")?;
19544    extract_function_declarator(declarator)?;
19545    let mut cursor = node.walk();
19546    let head = node
19547        .named_children(&mut cursor)
19548        .take_while(|child| *child != type_node)
19549        .filter(|child| child.kind() != "comment")
19550        .collect::<Vec<_>>();
19551    let [type_modifier, name_modifier] = head.as_slice() else {
19552        return None;
19553    };
19554    macro_modifier_identifier(*type_modifier)?;
19555    macro_modifier_identifier(*name_modifier)?;
19556    Some((*type_modifier, *name_modifier))
19557}
19558
19559/// The stand-in field evidence in a clean macro-return function definition:
19560/// `CUTLASS_DEVICE static OperandLayout Layout() { ... }` has a
19561/// `macro_modifier` head and a return type naming an active template type
19562/// parameter. The older grammar read the same head as a phantom
19563/// `field_declaration` named after the type, and dependent calls like
19564/// `OperandLayout::packed` resolved to that field (#1129). Returns the head's
19565/// first token and the type node; the active-parameter gate is the same one
19566/// the phantom-field suppression used, so a macro return type naming no
19567/// template parameter (`result_type`) recovers nothing.
19568fn macro_return_template_parameter_field<'tree>(
19569    node: Node<'tree>,
19570    source: &str,
19571    ancestry: &ParentIndex<'tree>,
19572) -> Option<(Node<'tree>, Node<'tree>)> {
19573    if node.kind() != "function_definition" {
19574        return None;
19575    }
19576    let type_node = node.child_by_field_name("type")?;
19577    if type_node.kind() != "type_identifier" {
19578        return None;
19579    }
19580    let declarator = node.child_by_field_name("declarator")?;
19581    extract_function_declarator(declarator)?;
19582    let mut cursor = node.walk();
19583    let head = node
19584        .named_children(&mut cursor)
19585        .take_while(|child| *child != type_node)
19586        .filter(|child| child.kind() != "comment")
19587        .collect::<Vec<_>>();
19588    let [first, ..] = head.as_slice() else {
19589        return None;
19590    };
19591    if !head
19592        .iter()
19593        .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19594        || !head.iter().any(|child| child.kind() == "macro_modifier")
19595    {
19596        return None;
19597    }
19598    let name = normalize_cpp_whitespace(node_text(type_node, source));
19599    if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19600        return None;
19601    }
19602    Some((*first, type_node))
19603}
19604
19605/// Whether `name` is a type parameter of a template declaration that lexically
19606/// encloses `node`. The malformed macro-return field uses the parameter name as
19607/// its pseudo-declarator; preserving that field is necessary to publish a
19608/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
19609/// ancestors instead of interpreting source text so nested templates and
19610/// parser-recovered regions retain their real lexical scopes.
19611pub(crate) fn cpp_active_template_type_parameter<'tree>(
19612    node: Node<'tree>,
19613    name: &str,
19614    source: &str,
19615    ancestry: &ParentIndex<'tree>,
19616) -> bool {
19617    let mut ancestor = ancestry.parent(node);
19618    while let Some(current) = ancestor {
19619        if current.kind() == "template_declaration"
19620            && let Some(parameters) = current.child_by_field_name("parameters")
19621            && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19622                cpp_template_parameter_name(parameter, source)
19623            })
19624            .type_parameter
19625        {
19626            return true;
19627        }
19628        ancestor = ancestry.parent(current);
19629    }
19630    false
19631}
19632
19633/// Reparse the region `[start, end)` of `source` as C++, confined to the region
19634/// via included ranges so every reparsed node keeps its original byte offset and
19635/// line number. The existing visitors read node text from the original source,
19636/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
19637/// `parse_rust_region_tree` technique.
19638fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19639    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19640}
19641
19642fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19643    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19644        return None;
19645    }
19646    let semicolon = node.next_sibling()?;
19647    if semicolon.kind() != ";" || semicolon.is_missing() {
19648        return None;
19649    }
19650    let row = node.start_position().row;
19651    let mut start = node.start_byte();
19652    let mut sibling = node.prev_sibling();
19653    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19654        if previous.kind() == ";" {
19655            break;
19656        }
19657        start = previous.start_byte();
19658        sibling = previous.prev_sibling();
19659    }
19660    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19661}
19662
19663/// One `RET name _(( args ));`-style K&R prototype-macro invocation
19664/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
19665/// byte spans a clean reparse needs, in original-source order.
19666struct PrototypeMacroCandidate {
19667    /// Where the return type and declared name begin.
19668    run_start: usize,
19669    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
19670    /// exclusive end of the first reparse span.
19671    identifier_start: usize,
19672    /// The inner `(`'s start byte: the start of the second reparse span.
19673    /// This paren is kept so the reparse sees exactly one parameter-list
19674    /// paren pair.
19675    inner_open_start: usize,
19676    /// Byte just past the inner `)`: the end of the second reparse span.
19677    inner_close_end: usize,
19678    /// Byte just past the outer `)`: the start of the third reparse span.
19679    outer_close_end: usize,
19680    /// Byte just past the terminating `;`: the end of the third reparse
19681    /// span, and of the whole candidate.
19682    semicolon_end: usize,
19683}
19684
19685impl PrototypeMacroCandidate {
19686    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
19687    /// one included-range tree: the return type and name, the parenthesized
19688    /// argument list, and the terminating `;`. The macro token and its outer
19689    /// wrapping parenthesis are omitted -- deleting them is exactly what the
19690    /// (absent) preprocessor macro expansion would do.
19691    fn ranges(&self) -> [(usize, usize); 3] {
19692        [
19693            (self.run_start, self.identifier_start),
19694            (self.inner_open_start, self.inner_close_end),
19695            (self.outer_close_end, self.semicolon_end),
19696        ]
19697    }
19698}
19699
19700/// The class a class-body prototype-macro reparse keeps around the member
19701/// (#6902): the class head through its `{`, the access label the member is
19702/// written under, and the class's `}`, at their original offsets. In that
19703/// parse the recovered prototype is a real member of a real class body, so its
19704/// `static` and its place on the body's access ladder state its modifiers
19705/// exactly as an ordinary member's do.
19706struct PrototypeMacroClassEnvelope<'tree> {
19707    /// The class's enclosing node, which is the recovery's origin.
19708    class_parent: Node<'tree>,
19709    /// The class head through the body's `{`.
19710    head: (usize, usize),
19711    /// The body's `{`, which finds the reparsed body.
19712    open: (usize, usize),
19713    /// The last `access_specifier` the body writes before the member, through
19714    /// its `:`.
19715    label: Option<(usize, usize)>,
19716    /// The body's `}`.
19717    close: (usize, usize),
19718}
19719
19720/// The [`PrototypeMacroClassEnvelope`] of one class-body member, read forward
19721/// with one cursor as [`cpp_member_declared_visibility`] reads the access
19722/// ladder. A member nested in anything but preprocessor conditionals, or under
19723/// a label a macro invocation swallowed, has no envelope that reproduces its
19724/// position, so it stays unrecovered rather than taking another access.
19725fn cpp_prototype_macro_class_envelope<'tree>(
19726    member: Node<'tree>,
19727    source: &str,
19728    ancestry: &ParentIndex<'tree>,
19729) -> Option<PrototypeMacroClassEnvelope<'tree>> {
19730    let mut list_level = member;
19731    let body = loop {
19732        let parent = ancestry.parent(list_level)?;
19733        if parent.kind() == "field_declaration_list" {
19734            break parent;
19735        }
19736        if !is_cpp_preprocessor_conditional(parent) {
19737            return None;
19738        }
19739        list_level = parent;
19740    };
19741    let class_like = ancestry.parent(body)?;
19742    let class_parent = ancestry.parent(class_like)?;
19743    let open = body.child(0).filter(|open| open.kind() == "{")?;
19744    let close = body
19745        .child(body.child_count().checked_sub(1)?)
19746        .filter(|close| close.kind() == "}" && !close.is_missing())?;
19747    let mut label = None;
19748    let mut under_swallowed_label = false;
19749    let mut cursor = body.walk();
19750    for child in body.children(&mut cursor) {
19751        if child == list_level {
19752            break;
19753        }
19754        if child.kind() == "access_specifier" {
19755            // The grammar leaves the label's `:` to the body.
19756            let colon = child
19757                .next_sibling()
19758                .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
19759            label = Some((child.start_byte(), colon.end_byte()));
19760            under_swallowed_label = false;
19761        } else if cpp_macro_swallowed_access_label(child, source).is_some() {
19762            under_swallowed_label = true;
19763        }
19764    }
19765    if under_swallowed_label {
19766        return None;
19767    }
19768    Some(PrototypeMacroClassEnvelope {
19769        class_parent,
19770        head: (class_like.start_byte(), open.end_byte()),
19771        open: (open.start_byte(), open.end_byte()),
19772        label,
19773        close: (close.start_byte(), close.end_byte()),
19774    })
19775}
19776
19777/// A live terminating semicolon owned directly by `node`.
19778fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19779    node.child(node.child_count().checked_sub(1)?)
19780        .filter(|child| child.kind() == ";" && !child.is_missing())
19781}
19782
19783/// The known pre-ANSI prototype macros whose expansion is exactly their one
19784/// argument. The malformed CST cannot prove that an arbitrary identifier has
19785/// that definition, so unknown spellings fail closed rather than turning an
19786/// ordinary broken declaration into a Function (issue #2932).
19787fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19788    matches!(
19789        node.kind(),
19790        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19791    ) && matches!(
19792        normalize_cpp_whitespace(node_text(node, source)).as_str(),
19793        "_" | "__P" | "OF" | "PROTO"
19794    )
19795}
19796
19797/// Read tree-sitter's recovery for `declared_name MACRO`: a
19798/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
19799/// declared name, and whose `name` is a known prototype macro.
19800fn cpp_prototype_macro_qualified_parts<'tree>(
19801    node: Node<'tree>,
19802    source: &str,
19803) -> Option<(Node<'tree>, Node<'tree>)> {
19804    if node.kind() != "qualified_identifier" {
19805        return None;
19806    }
19807    let declared_name = node
19808        .child_by_field_name("scope")
19809        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19810    let macro_name = macro_decorated_unqualified_name(node)?;
19811    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19812}
19813
19814/// The inner parenthesized node of tree-sitter's structured
19815/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
19816/// the macro invocation's outer parentheses while retaining the one pair an
19817/// ordinary function declarator needs. No delimiter search is involved: both
19818/// pairs and their ownership come from the CST.
19819fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19820    if arguments.kind() != "argument_list"
19821        || arguments.named_child_count() != 1
19822        || arguments.child_count() != 3
19823        || arguments
19824            .child(0)
19825            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19826        || arguments
19827            .child(2)
19828            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19829    {
19830        return None;
19831    }
19832    let inner = arguments.named_child(0)?;
19833    let close_index = match inner.kind() {
19834        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19835        // tree-sitter completes the C++ cast production with a zero-width
19836        // value after the live close parenthesis.
19837        "cast_expression" => inner.child_count().checked_sub(2)?,
19838        _ => return None,
19839    };
19840    (inner
19841        .child(0)
19842        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19843        && inner
19844            .child(close_index)
19845            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19846    .then_some(inner)
19847}
19848
19849fn cpp_prototype_macro_candidate_from_init_declaration(
19850    declaration: Node<'_>,
19851    source: &str,
19852) -> Option<PrototypeMacroCandidate> {
19853    let init = declaration
19854        .child_by_field_name("declarator")
19855        .filter(|declarator| declarator.kind() == "init_declarator")?;
19856    let malformed_declarator = init.child_by_field_name("declarator")?;
19857    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19858        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19859    } else {
19860        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19861            return None;
19862        }
19863        let declared_name_error = init
19864            .prev_named_sibling()
19865            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19866        let declared_name = declared_name_error
19867            .named_child(0)
19868            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19869        (declared_name, malformed_declarator)
19870    };
19871    let arguments = init
19872        .child_by_field_name("value")
19873        .filter(|value| value.kind() == "argument_list")?;
19874    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19875    let semicolon = cpp_direct_semicolon(declaration)?;
19876    let return_type = declaration.child_by_field_name("type")?;
19877    if return_type.end_byte() > declared_name.start_byte()
19878        || declared_name.end_byte() > macro_name.start_byte()
19879        || macro_name.end_byte() > arguments.start_byte()
19880        || arguments.end_byte() > semicolon.start_byte()
19881    {
19882        return None;
19883    }
19884    Some(cpp_prototype_macro_candidate_from_parts(
19885        declaration.start_byte(),
19886        macro_name,
19887        inner,
19888        arguments,
19889        semicolon,
19890    ))
19891}
19892
19893fn cpp_prototype_macro_candidate_from_qualified_declaration(
19894    declaration: Node<'_>,
19895    source: &str,
19896) -> Option<PrototypeMacroCandidate> {
19897    let qualified = declaration
19898        .child_by_field_name("declarator")
19899        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19900    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19901    let open_error = qualified
19902        .next_named_sibling()
19903        .filter(|next| next.kind() == "ERROR")?;
19904    let close_error = last_named_child(declaration)
19905        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19906    if open_error.child_count() < 3
19907        || open_error
19908            .child(0)
19909            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19910        || open_error
19911            .child(1)
19912            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19913        || close_error.child_count() != 2
19914        || close_error
19915            .child(0)
19916            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19917        || close_error
19918            .child(1)
19919            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19920    {
19921        return None;
19922    }
19923    let inner_open = open_error.child(1)?;
19924    let inner_close = close_error.child(0)?;
19925    let outer_close = close_error.child(1)?;
19926    let semicolon = cpp_direct_semicolon(declaration)?;
19927    let return_type = declaration.child_by_field_name("type")?;
19928    if return_type.end_byte() > qualified.start_byte()
19929        || macro_name.end_byte() > open_error.start_byte()
19930        || inner_open.start_byte() > inner_close.end_byte()
19931        || inner_close.end_byte() > outer_close.start_byte()
19932        || outer_close.end_byte() > semicolon.start_byte()
19933    {
19934        return None;
19935    }
19936    Some(PrototypeMacroCandidate {
19937        run_start: declaration.start_byte(),
19938        identifier_start: macro_name.start_byte(),
19939        inner_open_start: inner_open.start_byte(),
19940        inner_close_end: inner_close.end_byte(),
19941        outer_close_end: outer_close.end_byte(),
19942        semicolon_end: semicolon.end_byte(),
19943    })
19944}
19945
19946fn cpp_prototype_macro_candidate_from_pointer_expression(
19947    statement: Node<'_>,
19948    source: &str,
19949) -> Option<PrototypeMacroCandidate> {
19950    if statement.kind() != "expression_statement"
19951        || statement.named_child_count() != 1
19952        || !statement.has_error()
19953    {
19954        return None;
19955    }
19956    let expansion = statement
19957        .named_child(0)
19958        .filter(|child| child.kind() == "parameter_pack_expansion")?;
19959    let binary = expansion
19960        .child_by_field_name("pattern")
19961        .filter(|pattern| pattern.kind() == "binary_expression")?;
19962    if binary.child_count() != 3
19963        || binary
19964            .child(1)
19965            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19966        || expansion
19967            .child(expansion.child_count().checked_sub(1)?)
19968            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19969    {
19970        return None;
19971    }
19972    let return_type = binary.child_by_field_name("left")?;
19973    let call = binary
19974        .child_by_field_name("right")
19975        .filter(|right| right.kind() == "call_expression")?;
19976    let qualified = call.child_by_field_name("function")?;
19977    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19978    let arguments = call
19979        .child_by_field_name("arguments")
19980        .filter(|arguments| arguments.kind() == "argument_list")?;
19981    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19982    let semicolon = cpp_direct_semicolon(statement)?;
19983    if return_type.end_byte() > declared_name.start_byte()
19984        || macro_name.end_byte() > arguments.start_byte()
19985        || arguments.end_byte() > semicolon.start_byte()
19986    {
19987        return None;
19988    }
19989    Some(cpp_prototype_macro_candidate_from_parts(
19990        statement.start_byte(),
19991        macro_name,
19992        inner,
19993        arguments,
19994        semicolon,
19995    ))
19996}
19997
19998/// The inner parenthesized declarator of the `((parameters))` wrapper the
19999/// macro-declaration grammar makes of a prototype macro's argument list, with
20000/// the same span-keeping contract as [`cpp_prototype_macro_inner_arguments`]:
20001/// both paren pairs and their ownership come from the CST, and the returned
20002/// node's exact range is the one pair an ordinary function declarator needs.
20003fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
20004    if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
20005        return None;
20006    }
20007    let open = outer.child(0)?;
20008    let close = outer.child(outer.child_count().checked_sub(1)?)?;
20009    if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
20010        return None;
20011    }
20012    let inner = outer.named_child(0)?;
20013    if inner.kind() != "parenthesized_declarator" {
20014        return None;
20015    }
20016    let inner_open = inner.child(0)?;
20017    let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
20018    (inner_open.kind() == "("
20019        && !inner_open.is_missing()
20020        && inner_close.kind() == ")"
20021        && !inner_close.is_missing())
20022    .then_some(inner)
20023}
20024
20025/// Whether the macro-declaration grammar admitted a word of this declaration
20026/// as a `macro_modifier`, the structured proof that the declaration did not
20027/// parse without it.
20028fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
20029    let mut cursor = declaration.walk();
20030    declaration
20031        .named_children(&mut cursor)
20032        .any(|child| child.kind() == "macro_modifier")
20033}
20034
20035/// `RET name _((args));` as the macro-declaration grammar reads it when the
20036/// argument list stays an expression: the return-type word is admitted as a
20037/// `macro_modifier`, the declared name takes the `type` field, and the macro
20038/// token is the init declarator's callee. Same #2932 contract as the
20039/// ERROR-based readers above, on nodes that carry no `ERROR` of their own.
20040fn cpp_prototype_macro_candidate_from_macro_modifier_init(
20041    declaration: Node<'_>,
20042    source: &str,
20043) -> Option<PrototypeMacroCandidate> {
20044    if !cpp_declaration_has_macro_modifier(declaration) {
20045        return None;
20046    }
20047    let declared_name = declaration
20048        .child_by_field_name("type")
20049        .filter(|type_node| type_node.kind() == "type_identifier")?;
20050    let init = declaration
20051        .child_by_field_name("declarator")
20052        .filter(|declarator| declarator.kind() == "init_declarator")?;
20053    let macro_name = init
20054        .child_by_field_name("declarator")
20055        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20056    let arguments = init
20057        .child_by_field_name("value")
20058        .filter(|value| value.kind() == "argument_list")?;
20059    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20060    let semicolon = cpp_direct_semicolon(declaration)?;
20061    if declared_name.end_byte() > macro_name.start_byte()
20062        || macro_name.end_byte() > arguments.start_byte()
20063        || arguments.end_byte() > semicolon.start_byte()
20064    {
20065        return None;
20066    }
20067    Some(cpp_prototype_macro_candidate_from_parts(
20068        declaration.start_byte(),
20069        macro_name,
20070        inner,
20071        arguments,
20072        semicolon,
20073    ))
20074}
20075
20076/// `RET *name _((args));` as the macro-declaration grammar reads it when the
20077/// argument list stays declarator-shaped: the declared name and the macro
20078/// token prefix the nested parenthesized declarator as the modifiers of a
20079/// `macro_modifier_declarator` chain.
20080fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
20081    declaration: Node<'_>,
20082    source: &str,
20083) -> Option<PrototypeMacroCandidate> {
20084    let mut declarator = declaration.child_by_field_name("declarator")?;
20085    while declarator.kind() == "pointer_declarator" {
20086        declarator = declarator.child_by_field_name("declarator")?;
20087    }
20088    // The macro's own modifier must be preceded by the declared name's.
20089    let mut saw_name_modifier = false;
20090    let (macro_name, arguments) = loop {
20091        if declarator.kind() != "macro_modifier_declarator" {
20092            return None;
20093        }
20094        let mut cursor = declarator.walk();
20095        let modifier = declarator
20096            .named_children(&mut cursor)
20097            .find(|child| child.kind() == "macro_modifier")
20098            .and_then(macro_modifier_identifier)?;
20099        let inner = declarator.child_by_field_name("declarator")?;
20100        if cpp_is_prototype_macro_identifier(modifier, source) {
20101            break (modifier, inner);
20102        }
20103        saw_name_modifier = true;
20104        declarator = inner;
20105    };
20106    if !saw_name_modifier {
20107        return None;
20108    }
20109    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20110    let semicolon = cpp_direct_semicolon(declaration)?;
20111    Some(cpp_prototype_macro_candidate_from_parts(
20112        declaration.start_byte(),
20113        macro_name,
20114        inner,
20115        arguments,
20116        semicolon,
20117    ))
20118}
20119
20120/// `RET name _((args));` as the macro-declaration grammar reads it when the
20121/// macro token is the one word that could stand as the type: the return-type
20122/// words and the declared name are `macro_modifier`s and the nested argument
20123/// list is the whole declarator.
20124fn cpp_prototype_macro_candidate_from_macro_modifier_type(
20125    declaration: Node<'_>,
20126    source: &str,
20127) -> Option<PrototypeMacroCandidate> {
20128    if !cpp_declaration_has_macro_modifier(declaration) {
20129        return None;
20130    }
20131    let macro_name = declaration
20132        .child_by_field_name("type")
20133        .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
20134    let arguments = declaration.child_by_field_name("declarator")?;
20135    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20136    let semicolon = cpp_direct_semicolon(declaration)?;
20137    Some(cpp_prototype_macro_candidate_from_parts(
20138        declaration.start_byte(),
20139        macro_name,
20140        inner,
20141        arguments,
20142        semicolon,
20143    ))
20144}
20145
20146/// `RET name _((args));` as the macro-declaration grammar reads it when the
20147/// argument list itself needs recovery: the return-type word is a
20148/// `macro_modifier`, the declared name takes the `type` field, the macro token
20149/// is the first declarator, and the argument list's paren pairs land in the
20150/// bracketing `ERROR`s.
20151fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
20152    declaration: Node<'_>,
20153    source: &str,
20154) -> Option<PrototypeMacroCandidate> {
20155    if !cpp_declaration_has_macro_modifier(declaration) {
20156        return None;
20157    }
20158    let declared_name = declaration
20159        .child_by_field_name("type")
20160        .filter(|type_node| type_node.kind() == "type_identifier")?;
20161    let mut declarator_cursor = declaration.walk();
20162    let macro_name = declaration
20163        .children_by_field_name("declarator", &mut declarator_cursor)
20164        .next()
20165        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20166    let open_error = macro_name
20167        .next_named_sibling()
20168        .filter(|next| next.kind() == "ERROR")?;
20169    let close_error = last_named_child(declaration)
20170        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20171    if open_error.child_count() < 3
20172        || open_error
20173            .child(0)
20174            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20175        || open_error
20176            .child(1)
20177            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20178        || close_error.child_count() != 2
20179        || close_error
20180            .child(0)
20181            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20182        || close_error
20183            .child(1)
20184            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20185    {
20186        return None;
20187    }
20188    let inner_open = open_error.child(1)?;
20189    let inner_close = close_error.child(0)?;
20190    let outer_close = close_error.child(1)?;
20191    let semicolon = cpp_direct_semicolon(declaration)?;
20192    if declared_name.end_byte() > macro_name.start_byte()
20193        || macro_name.end_byte() > open_error.start_byte()
20194        || inner_open.start_byte() > inner_close.end_byte()
20195        || inner_close.end_byte() > outer_close.start_byte()
20196        || outer_close.end_byte() > semicolon.start_byte()
20197    {
20198        return None;
20199    }
20200    Some(PrototypeMacroCandidate {
20201        run_start: declaration.start_byte(),
20202        identifier_start: macro_name.start_byte(),
20203        inner_open_start: inner_open.start_byte(),
20204        inner_close_end: inner_close.end_byte(),
20205        outer_close_end: outer_close.end_byte(),
20206        semicolon_end: semicolon.end_byte(),
20207    })
20208}
20209
20210fn cpp_prototype_macro_candidate_from_parts(
20211    run_start: usize,
20212    macro_name: Node<'_>,
20213    inner_arguments: Node<'_>,
20214    outer_arguments: Node<'_>,
20215    semicolon: Node<'_>,
20216) -> PrototypeMacroCandidate {
20217    PrototypeMacroCandidate {
20218        run_start,
20219        identifier_start: macro_name.start_byte(),
20220        inner_open_start: inner_arguments.start_byte(),
20221        inner_close_end: inner_arguments.end_byte(),
20222        outer_close_end: outer_arguments.end_byte(),
20223        semicolon_end: semicolon.end_byte(),
20224    }
20225}
20226
20227/// Recover only CST shapes whose fields preserve the declaration name, macro
20228/// invocation, nested argument list, and terminator. Arbitrary flattened
20229/// `ERROR` wreckage stays unsupported rather than being interpreted through a
20230/// token or delimiter scan (issue #2932).
20231fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20232    let candidate = match node.kind() {
20233        "declaration" | "field_declaration" => {
20234            cpp_prototype_macro_candidate_from_init_declaration(node, source)
20235                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20236                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20237                .or_else(|| {
20238                    cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20239                })
20240                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20241                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20242        }
20243        "expression_statement" => {
20244            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20245        }
20246        _ => None,
20247    };
20248    candidate.into_iter().collect()
20249}
20250
20251fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20252    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20253        return false;
20254    }
20255    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20256        return false;
20257    }
20258    let Some(declarator) = (if node.kind() == "function_definition" {
20259        node.child_by_field_name("declarator")
20260            .and_then(extract_function_declarator)
20261    } else {
20262        node.named_child(0)
20263            .filter(|child| child.kind() == "function_declarator")
20264    }) else {
20265        return false;
20266    };
20267    let Some(name) = cpp_function_declarator_name_node(declarator) else {
20268        return false;
20269    };
20270    declarator.start_byte() == node.start_byte()
20271        && name.kind() == "identifier"
20272        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20273}
20274
20275/// Reparse a fragmented class-body interior while preserving its original byte
20276/// and line offsets, confined to the region by tree-sitter included ranges.
20277///
20278/// This used to materialize the region's whole file prefix as whitespace and
20279/// make the lexer walk it, the technique #1309 replaced on the other reparse
20280/// path: O(file) per fragmented-class recovery, on files that are already
20281/// error-recovered and already slow (#2788). Included ranges give the parser
20282/// the same view -- the region's bytes, at their original offsets and
20283/// line/column positions -- without materializing or lexing anything before it.
20284///
20285/// The equality of the two views is the claim, so a debug build parses both and
20286/// asserts the trees agree node for node.
20287fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20288    let region = cpp_reparse_region_items(source, start, end);
20289
20290    #[cfg(debug_assertions)]
20291    assert_eq!(
20292        region.as_ref().map(cpp_tree_shape),
20293        cpp_reparse_padded_class_body(source, start, end)
20294            .as_ref()
20295            .map(cpp_tree_shape),
20296        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20297         prefix produces"
20298    );
20299
20300    region
20301}
20302
20303/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
20304/// replaces, kept as the oracle a debug build asserts every region reparse
20305/// against and as the release-mode parity tests' reference (#2788).
20306#[cfg(any(debug_assertions, test))]
20307fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20308    if start >= end {
20309        // The included-range parser refuses an empty region; the padded one
20310        // returned a tree holding nothing, which every caller read as "no
20311        // members here".
20312        return None;
20313    }
20314    let bytes = source.as_bytes();
20315    let prefix = bytes.get(..start)?;
20316    let interior = bytes.get(start..end)?;
20317    let mut padded = Vec::with_capacity(end);
20318    padded.extend(
20319        prefix
20320            .iter()
20321            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20322    );
20323    padded.extend_from_slice(interior);
20324    let padded = String::from_utf8(padded).ok()?;
20325    let mut parser = tree_sitter::Parser::new();
20326    parser
20327        .set_language(&tree_sitter_cpp::LANGUAGE.into())
20328        .ok()?;
20329    parser.parse(&padded, None)
20330}
20331
20332/// Every node of `tree` in preorder, by kind, span and position, which is what
20333/// two reparses of one region have to agree on for their callers to read the
20334/// same declarations out of either (#2788).
20335#[cfg(any(debug_assertions, test))]
20336fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20337    let mut shape = Vec::new();
20338    let mut cursor = tree.root_node().walk();
20339    let mut stack = vec![tree.root_node()];
20340    while let Some(node) = stack.pop() {
20341        shape.push((
20342            node.kind(),
20343            node.start_byte(),
20344            node.end_byte(),
20345            node.start_position().row,
20346            node.start_position().column,
20347            node.is_named(),
20348            node.is_missing(),
20349        ));
20350        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20351        stack.extend(children.into_iter().rev());
20352    }
20353    shape
20354}
20355
20356/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
20357/// reparsed interior is indexed only when every top-level named node is a
20358/// well-formed C++ item (or a comment) and at least one real item is present.
20359/// Expression/statement soup surfaces as a top-level `ERROR` or
20360/// `expression_statement`, neither of which is an item kind, so it is rejected.
20361///
20362/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
20363/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
20364/// swallowed by a preceding dangling sentinel) reparses to a real
20365/// `namespace_definition` whose body still holds a bogus `function_definition`,
20366/// so the subtree legitimately carries an error. Container items are admitted
20367/// even with an internal error; the inner bogus function is recovered recursively
20368/// when `visit_function_definition` walks it. Each recursion strips at least one
20369/// leading sentinel, so the region strictly shrinks and recovery terminates.
20370///
20371/// A top-level `function_definition` is the one place we stay strict: it is
20372/// admitted only when it is clean or is itself a sentinel candidate. A function
20373/// that has an error and is not a sentinel is a real callable with a broken body,
20374/// so we refuse the whole reparse and let the ordinary path handle it (preserving
20375/// its real return type rather than re-deriving an implicit one).
20376fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20377    let mut cursor = root.walk();
20378    let mut saw_item = false;
20379    for child in root.named_children(&mut cursor) {
20380        match child.kind() {
20381            "comment" => {}
20382            "function_definition" => {
20383                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20384                    return false;
20385                }
20386                saw_item = true;
20387            }
20388            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20389            _ => return false,
20390        }
20391    }
20392    saw_item
20393}
20394
20395/// Robustness gate for a reparsed fragmented multiple-base export class body
20396/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
20397/// kinds a class body produces when reparsed at translation-unit scope: the
20398/// access-specifier label preceding the first member surfaces as a
20399/// `labeled_statement` wrapping that member, and members surface as
20400/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
20401/// Statement or expression soup surfaces as other top-level kinds and is rejected,
20402/// so only a genuinely member-shaped body is ever re-owned as members; anything
20403/// ambiguous falls back to indexing the class alone.
20404fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20405    if node.kind() != "ERROR" {
20406        return false;
20407    }
20408    let mut stack = Vec::new();
20409    let mut saw_function_declarator = false;
20410    let mut cursor = node.walk();
20411    for child in node.named_children(&mut cursor) {
20412        stack.push(child);
20413    }
20414    while let Some(current) = stack.pop() {
20415        match current.kind() {
20416            // Tree-sitter may wrap adjacent copy-control declarations in a
20417            // nested ERROR. Keep descending only through ERROR wrappers; the
20418            // actual declaration payload must be a function_declarator.
20419            "ERROR" => {
20420                let mut cursor = current.walk();
20421                stack.extend(current.named_children(&mut cursor));
20422            }
20423            "function_declarator" => saw_function_declarator = true,
20424            _ => return false,
20425        }
20426    }
20427    saw_function_declarator
20428}
20429
20430fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20431    if node.kind() != "ERROR" {
20432        return false;
20433    }
20434    let mut cursor = node.walk();
20435    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20436    let [explicit, constructor_error, tail] = named.as_slice() else {
20437        return false;
20438    };
20439    if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20440        return false;
20441    }
20442    // The copy-control declarators the inner ERROR holds, with only their `;`
20443    // tokens beside them. The older grammar kept the constructor alone there
20444    // and gave the destructor the slot after it; the macro-declaration grammar
20445    // absorbs the destructor beside the constructor and folds the member
20446    // written after them into the tail slot as a `macro_modifier_declarator`.
20447    let mut declarators = Vec::new();
20448    let mut stack = vec![*constructor_error];
20449    while let Some(current) = stack.pop() {
20450        match current.kind() {
20451            "ERROR" => {
20452                let mut cursor = current.walk();
20453                let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20454                stack.extend(children.into_iter().rev());
20455            }
20456            "function_declarator" => declarators.push(current),
20457            _ => return false,
20458        }
20459    }
20460    let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20461        ([constructor], "function_declarator") => (*constructor, *tail),
20462        ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20463        _ => return false,
20464    };
20465    let Some(constructor_name) =
20466        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20467    else {
20468        return false;
20469    };
20470    let Some(destructor_name) =
20471        extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20472    else {
20473        return false;
20474    };
20475    let Some(destroyed_type) = destructor_name.named_child(0) else {
20476        return false;
20477    };
20478    constructor_name.kind() == "identifier"
20479        && destructor_name.kind() == "destructor_name"
20480        && destroyed_type.kind() == "identifier"
20481        && node_text(constructor_name, source) == node_text(destroyed_type, source)
20482}
20483
20484fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20485    if node.kind() != "compound_statement" {
20486        return false;
20487    }
20488    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20489        return false;
20490    };
20491    if prefix.kind() == "labeled_statement"
20492        && prefix.named_child(0).is_some_and(|label| {
20493            matches!(
20494                node_text(label, source).trim(),
20495                "public" | "private" | "protected"
20496            )
20497        })
20498    {
20499        return prefix.named_children(&mut prefix.walk()).any(|child| {
20500            child.kind() == "declaration"
20501                && child.has_error()
20502                && child
20503                    .named_children(&mut child.walk())
20504                    .any(cpp_reparsed_member_error_is_indexable)
20505        });
20506    }
20507    // A malformed constructor initializer can be split into a declaration
20508    // followed by its compound body when the class prefix already contains
20509    // realistic members. Keep this admission tied to that exact structured
20510    // declaration/error/body chain rather than accepting arbitrary blocks.
20511    prefix.kind() == "declaration"
20512        && prefix.has_error()
20513        && prefix
20514            .named_children(&mut prefix.walk())
20515            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20516}
20517
20518fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20519    if !cpp_reparsed_member_error_is_indexable(node) {
20520        return false;
20521    }
20522    let Some(preproc) = node.next_named_sibling() else {
20523        return false;
20524    };
20525    preproc.kind() == "preproc_if"
20526        && preproc.has_error()
20527        && preproc
20528            .named_children(&mut preproc.walk())
20529            .any(|child| child.kind() == "expression_statement" && child.has_error())
20530        && preproc
20531            .next_named_sibling()
20532            .is_some_and(|body| body.kind() == "compound_statement")
20533}
20534
20535/// Return a function body whose braces and ownership are explicit in the
20536/// reparsed class-member tree. An error below a real function envelope is
20537/// recoverable by the ordinary function visitor; a missing/deferred body is
20538/// not, because accepting it would let statement soup masquerade as a member.
20539fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20540    if node.kind() != "function_definition" {
20541        return None;
20542    }
20543    let body = node.child_by_field_name("body")?;
20544    if body.kind() != "compound_statement" {
20545        return None;
20546    }
20547    let open = body.child(0)?;
20548    let close = body.child(body.child_count().checked_sub(1)?)?;
20549    if open.kind() != "{"
20550        || open.is_missing()
20551        || close.kind() != "}"
20552        || close.is_missing()
20553        || close.end_byte() != body.end_byte()
20554        || body.end_byte() != node.end_byte()
20555    {
20556        return None;
20557    }
20558    Some(body)
20559}
20560
20561fn cpp_reparsed_member_function_errors_are_in_body(
20562    node: Node<'_>,
20563    body: Node<'_>,
20564    source: &str,
20565) -> bool {
20566    let mut cursor = node.walk();
20567    node.children(&mut cursor).all(|child| {
20568        same_node(child, body)
20569            || cpp_reparsed_member_attribute_error(child, source)
20570            || cpp_reparsed_member_signature_identifier_errors(child)
20571            || (!child.has_error() && !child.is_error() && !child.is_missing())
20572    })
20573}
20574
20575/// A complete callable can still carry parser errors in its signature when a
20576/// project annotation is not part of the C++ grammar (`nonneg int`,
20577/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
20578/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
20579/// leaves inside the already-proven callable envelope; structured statements,
20580/// literals, missing tokens, and other malformed signature payload remain
20581/// rejected.
20582fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20583    if !node.has_error() && !node.is_error() && !node.is_missing() {
20584        return false;
20585    }
20586    let mut stack = vec![node];
20587    let mut saw_error = false;
20588    while let Some(current) = stack.pop() {
20589        if current.is_missing() {
20590            return false;
20591        }
20592        if current.kind() == "ERROR" {
20593            saw_error = true;
20594            let mut cursor = current.walk();
20595            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20596            if children
20597                .iter()
20598                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20599            {
20600                return false;
20601            }
20602            stack.extend(children);
20603            continue;
20604        }
20605        let mut cursor = current.walk();
20606        stack.extend(current.children(&mut cursor));
20607    }
20608    saw_error
20609}
20610
20611fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20612    node.kind() == "ERROR"
20613        && node.named_child_count() == 1
20614        && node
20615            .named_child(0)
20616            .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20617}
20618
20619fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20620    node.kind() == "identifier"
20621        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20622}
20623
20624/// A C++ attribute placed between a member's declarator and body can make
20625/// tree-sitter expose the callable as
20626/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
20627/// Keep this admission tied to that exact node geometry. In particular, an
20628/// arbitrary ERROR or identifier before a compound statement is not enough.
20629fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20630    let Some(body) = cpp_reparsed_member_function_body(node) else {
20631        return false;
20632    };
20633    let mut cursor = node.walk();
20634    let named = node
20635        .named_children(&mut cursor)
20636        .filter(|child| child.kind() != "comment")
20637        .collect::<Vec<_>>();
20638    let [type_node, error, attribute, body_node] = named.as_slice() else {
20639        return false;
20640    };
20641    if !same_node(*body_node, body)
20642        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20643        || !cpp_reparsed_export_macro_identifier(*attribute, source)
20644        || error.kind() != "ERROR"
20645        || error.named_child_count() != 1
20646    {
20647        return false;
20648    }
20649    error
20650        .named_child(0)
20651        .is_some_and(cpp_reparsed_attribute_callable_declarator)
20652}
20653
20654fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20655    cpp_structured_type_path(node, source).is_some()
20656        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20657}
20658
20659fn cpp_reparsed_member_return_and_declarator_are_indexable(
20660    return_error: Node<'_>,
20661    declarator: Node<'_>,
20662    body_node: Node<'_>,
20663    body: Node<'_>,
20664    source: &str,
20665) -> bool {
20666    return_error.kind() == "ERROR"
20667        && return_error.named_child_count() == 1
20668        && same_node(body_node, body)
20669        && return_error.named_child(0).is_some_and(|return_type| {
20670            cpp_reparsed_member_return_type_is_indexable(return_type, source)
20671        })
20672        && extract_function_declarator(declarator)
20673            .and_then(cpp_function_declarator_name_node)
20674            .is_some()
20675}
20676
20677fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20678    let Some(body) = cpp_reparsed_member_function_body(node) else {
20679        return false;
20680    };
20681    let mut cursor = node.walk();
20682    let named = node
20683        .named_children(&mut cursor)
20684        .filter(|child| child.kind() != "comment")
20685        .collect::<Vec<_>>();
20686    let [friend, return_error, declarator, body_node] = named.as_slice() else {
20687        return false;
20688    };
20689    friend.kind() == "type_identifier"
20690        && node_text(*friend, source) == "friend"
20691        && cpp_reparsed_member_return_and_declarator_are_indexable(
20692            *return_error,
20693            *declarator,
20694            *body_node,
20695            body,
20696            source,
20697        )
20698}
20699
20700fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20701    let Some(body) = cpp_reparsed_member_function_body(node) else {
20702        return false;
20703    };
20704    let mut cursor = node.walk();
20705    let named = node
20706        .named_children(&mut cursor)
20707        .filter(|child| child.kind() != "comment")
20708        .collect::<Vec<_>>();
20709    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20710        return false;
20711    };
20712    prefix
20713        .iter()
20714        .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20715        && attribute.kind() == "type_identifier"
20716        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20717        && cpp_reparsed_member_return_and_declarator_are_indexable(
20718            *return_error,
20719            *declarator,
20720            *body_node,
20721            body,
20722            source,
20723        )
20724}
20725
20726/// An included-range reparse that begins inside a malformed class can merge an
20727/// access label and following template member. Tree-sitter then emits the label
20728/// as the `template_type` name, the template parameter list as its arguments,
20729/// an ERROR-wrapped return type, the callable declarator, and its complete
20730/// body. Admit only that exact structured displacement.
20731fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20732    let Some(body) = cpp_reparsed_member_function_body(node) else {
20733        return false;
20734    };
20735    let mut cursor = node.walk();
20736    let named = node
20737        .named_children(&mut cursor)
20738        .filter(|child| child.kind() != "comment")
20739        .collect::<Vec<_>>();
20740    // The member's return type is an `ERROR` of its own, or -- under the
20741    // macro-declaration grammar, which admits it as a modifier of the
20742    // declarator it could not separate -- folded into the declarator's
20743    // `macro_modifier_declarator` wrapper.
20744    let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20745        [template_type, return_error, declarator, body_node] => {
20746            let Some(return_type) = return_error.named_child(0) else {
20747                return false;
20748            };
20749            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20750                return false;
20751            }
20752            (*template_type, return_type, *declarator, *body_node)
20753        }
20754        [template_type, declarator, body_node] => {
20755            let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20756                .then(|| {
20757                    let mut cursor = declarator.walk();
20758                    declarator
20759                        .named_children(&mut cursor)
20760                        .find(|child| child.kind() == "macro_modifier")
20761                })
20762                .flatten()
20763                .and_then(macro_modifier_identifier)
20764            else {
20765                return false;
20766            };
20767            let Some(function) = declarator.child_by_field_name("declarator") else {
20768                return false;
20769            };
20770            (*template_type, return_type, function, *body_node)
20771        }
20772        _ => return false,
20773    };
20774    let Some(template_name) = template_type.child_by_field_name("name") else {
20775        return false;
20776    };
20777    let Some(arguments) = template_type.child_by_field_name("arguments") else {
20778        return false;
20779    };
20780    let mut cursor = template_type.walk();
20781    let template_errors = template_type
20782        .named_children(&mut cursor)
20783        .filter(|child| child.kind() == "ERROR")
20784        .collect::<Vec<_>>();
20785    let [comment_error] = template_errors.as_slice() else {
20786        return false;
20787    };
20788    let mut cursor = comment_error.walk();
20789    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20790    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20791        return false;
20792    };
20793    same_node(body_node, body)
20794        && template_type.kind() == "template_type"
20795        && template_name.kind() == "type_identifier"
20796        && matches!(
20797            node_text(template_name, source).trim(),
20798            "public" | "private" | "protected"
20799        )
20800        && arguments.kind() == "template_argument_list"
20801        && arguments.named_child_count() > 0
20802        && !arguments.has_error()
20803        && !colon.is_named()
20804        && colon.kind() == ":"
20805        && comments.iter().all(|child| child.kind() == "comment")
20806        && !template_keyword.is_named()
20807        && template_keyword.kind() == "template"
20808        && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20809        && extract_function_declarator(declarator)
20810            .and_then(cpp_function_declarator_name_node)
20811            .is_some()
20812}
20813
20814/// Return the constructor declaration tree-sitter can merge into an access
20815/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
20816/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
20817/// declaration's apparent type; the callable name must still exactly match the
20818/// recovered class, so unrelated labeled statements are never re-owned.
20819fn cpp_reparsed_preprocessor_constructor<'tree>(
20820    node: Node<'tree>,
20821    class_name: &str,
20822    source: &str,
20823) -> Option<Node<'tree>> {
20824    if node.kind() != "labeled_statement" {
20825        return None;
20826    }
20827    let mut cursor = node.walk();
20828    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20829    let [label, directive_error, declaration] = named.as_slice() else {
20830        return None;
20831    };
20832    if label.kind() != "statement_identifier"
20833        || !matches!(
20834            node_text(*label, source),
20835            "public" | "private" | "protected"
20836        )
20837        || directive_error.kind() != "ERROR"
20838        || directive_error.child_count() != 1
20839        || directive_error
20840            .child(0)
20841            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20842        || declaration.kind() != "declaration"
20843        || declaration.named_child_count() != 2
20844    {
20845        return None;
20846    }
20847    let apparent_type = declaration.child_by_field_name("type")?;
20848    if apparent_type.kind() != "type_identifier"
20849        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20850    {
20851        return None;
20852    }
20853    let declarator = declaration.child_by_field_name("declarator")?;
20854    let function = extract_function_declarator(declarator)?;
20855    let name = cpp_function_declarator_name_node(function)?;
20856    (node_text(name, source) == class_name).then_some(*declaration)
20857}
20858
20859fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20860    if extract_function_declarator(node)
20861        .and_then(cpp_function_declarator_name_node)
20862        .is_some()
20863    {
20864        return true;
20865    }
20866    node.kind() == "init_declarator"
20867        && node
20868            .child_by_field_name("declarator")
20869            .is_some_and(|declarator| declarator.kind() == "identifier")
20870        && node
20871            .child_by_field_name("value")
20872            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20873}
20874
20875/// Return true for the constrained/attribute form that tree-sitter splits into
20876/// an ERROR declaration, a preprocessor `requires` clause, and a following
20877/// compound statement. The three nodes must remain immediate named siblings;
20878/// this deliberately does not search source text or skip unrelated statements.
20879fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20880    if node.kind() != "ERROR" || node.named_child_count() != 3 {
20881        return false;
20882    }
20883    let mut cursor = node.walk();
20884    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20885    let [type_node, function_declarator, attribute] = named.as_slice() else {
20886        return false;
20887    };
20888    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20889        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20890        || attribute.kind() != "identifier"
20891        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20892    {
20893        return false;
20894    }
20895    let Some(preproc) =
20896        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20897    else {
20898        return false;
20899    };
20900    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20901        .filter(|sibling| sibling.kind() == "compound_statement")
20902    else {
20903        return false;
20904    };
20905    let Some(open) = body.child(0) else {
20906        return false;
20907    };
20908    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20909        return false;
20910    };
20911    let Some(condition) = preproc.child_by_field_name("condition") else {
20912        return false;
20913    };
20914    let mut cursor = preproc.walk();
20915    let payload = preproc
20916        .named_children(&mut cursor)
20917        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20918        .collect::<Vec<_>>();
20919    let [requires_statement] = payload.as_slice() else {
20920        return false;
20921    };
20922    let requires_clause = requires_statement.named_child(0);
20923
20924    open.kind() == "{"
20925        && !open.is_missing()
20926        && close.kind() == "}"
20927        && !close.is_missing()
20928        && close.end_byte() == body.end_byte()
20929        && requires_statement.kind() == "expression_statement"
20930        && requires_statement.named_child_count() == 1
20931        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20932}
20933
20934fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20935    let mut sibling = node.next_named_sibling();
20936    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20937        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20938    }
20939    sibling
20940}
20941
20942fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20943    let mut sibling = node.prev_named_sibling();
20944    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20945        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20946    }
20947    sibling
20948}
20949
20950fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20951    let Some(preproc) =
20952        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20953    else {
20954        return false;
20955    };
20956    let Some(error) =
20957        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20958    else {
20959        return false;
20960    };
20961    cpp_reparsed_attribute_requires_error(error, source)
20962}
20963
20964fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20965    parameter: Node<'tree>,
20966    macro_name: Node<'tree>,
20967    message: Node<'tree>,
20968    source: &str,
20969) -> Option<Node<'tree>> {
20970    let parameter_name = parameter.named_child(0)?;
20971    (parameter.kind() == "type_parameter_declaration"
20972        && parameter_name.kind() == "type_identifier"
20973        && matches!(macro_name.kind(), "identifier" | "type_identifier")
20974        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20975        && message.kind() == "string_literal")
20976        .then_some(parameter_name)
20977}
20978
20979fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
20980    node: Node<'tree>,
20981    source: &str,
20982) -> Option<Node<'tree>> {
20983    if node.kind() != "ERROR" {
20984        return None;
20985    }
20986    let mut cursor = node.walk();
20987    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20988    let [parameter, macro_name, message] = named.as_slice() else {
20989        return None;
20990    };
20991    cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
20992}
20993
20994/// Recognize the alternate constraint-macro prefix where tree-sitter retains
20995/// the complete qualified constraint as a fourth child instead of moving it
20996/// into the following function. Keep the gate tied to a two-type template
20997/// constraint that names the declared type parameter.
20998fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
20999    node: Node<'tree>,
21000    source: &str,
21001) -> Option<Node<'tree>> {
21002    if node.kind() != "ERROR" {
21003        return None;
21004    }
21005    let mut cursor = node.walk();
21006    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21007    let [parameter, macro_name, message, constraint] = named.as_slice() else {
21008        return None;
21009    };
21010    let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
21011        *parameter,
21012        *macro_name,
21013        *message,
21014        source,
21015    )?;
21016    let constraint_scope = constraint.child_by_field_name("scope")?;
21017    let constraint_template = constraint.child_by_field_name("name")?;
21018    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21019    let mut argument_cursor = constraint_arguments.walk();
21020    let constraint_types = constraint_arguments
21021        .named_children(&mut argument_cursor)
21022        .collect::<Vec<_>>();
21023    if constraint.kind() != "qualified_identifier"
21024        || constraint_scope.kind() != "namespace_identifier"
21025        || !matches!(
21026            constraint_template.kind(),
21027            "template_function" | "template_type"
21028        )
21029        || !matches!(constraint_types.as_slice(), [left, right]
21030            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21031        || constraint_arguments.has_error()
21032    {
21033        return None;
21034    }
21035    let parameter_text = node_text(parameter_name, source);
21036    let mut stack = constraint_types;
21037    while let Some(current) = stack.pop() {
21038        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
21039            return Some(parameter_name);
21040        }
21041        let mut cursor = current.walk();
21042        stack.extend(current.named_children(&mut cursor));
21043    }
21044    None
21045}
21046
21047fn cpp_reparsed_template_macro_companion_is_indexable(
21048    node: Node<'_>,
21049    parameter_name: Node<'_>,
21050    source: &str,
21051) -> bool {
21052    let Some(body) = cpp_reparsed_member_function_body(node) else {
21053        return false;
21054    };
21055    let mut cursor = node.walk();
21056    let named = node
21057        .named_children(&mut cursor)
21058        .filter(|child| child.kind() != "comment")
21059        .collect::<Vec<_>>();
21060    // The member's return type is an `ERROR` of its own, or -- under the
21061    // macro-declaration grammar, which admits it as a modifier of the
21062    // declarator it could not separate -- folded into the declarator's
21063    // `macro_modifier_declarator` wrapper.
21064    let (constraint, close_error, storage, return_type, declarator, body_node) = match named
21065        .as_slice()
21066    {
21067        [
21068            constraint,
21069            close_error,
21070            storage,
21071            return_error,
21072            declarator,
21073            body_node,
21074        ] => {
21075            let Some(return_type) = return_error.named_child(0) else {
21076                return false;
21077            };
21078            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21079                return false;
21080            }
21081            (
21082                *constraint,
21083                *close_error,
21084                *storage,
21085                return_type,
21086                *declarator,
21087                *body_node,
21088            )
21089        }
21090        [constraint, close_error, storage, declarator, body_node] => {
21091            let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
21092                return false;
21093            };
21094            (
21095                *constraint,
21096                *close_error,
21097                *storage,
21098                return_type,
21099                function,
21100                *body_node,
21101            )
21102        }
21103        _ => return false,
21104    };
21105    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
21106        return false;
21107    };
21108    let Some(constraint_template) = constraint.child_by_field_name("name") else {
21109        return false;
21110    };
21111    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
21112        return false;
21113    };
21114    let mut cursor = constraint_arguments.walk();
21115    let constraint_types = constraint_arguments
21116        .named_children(&mut cursor)
21117        .collect::<Vec<_>>();
21118    same_node(body_node, body)
21119        && constraint.kind() == "qualified_identifier"
21120        && constraint_scope.kind() == "namespace_identifier"
21121        && constraint_template.kind() == "template_type"
21122        && matches!(constraint_types.as_slice(), [left, right]
21123            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21124        && !constraint_arguments.has_error()
21125        && close_error.kind() == "ERROR"
21126        && close_error.named_child_count() == 0
21127        && storage.kind() == "storage_class_specifier"
21128        && return_type.kind() == "identifier"
21129        && node_text(return_type, source) == node_text(parameter_name, source)
21130        && extract_function_declarator(declarator)
21131            .and_then(cpp_function_declarator_name_node)
21132            .is_some()
21133}
21134
21135fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
21136    node: Node<'tree>,
21137    parameter_name: Node<'_>,
21138    source: &str,
21139) -> Option<Node<'tree>> {
21140    let body = cpp_reparsed_member_function_body(node)?;
21141    let constraint = node.child_by_field_name("type")?;
21142    let constraint_template = constraint.child_by_field_name("name")?;
21143    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21144    let mut argument_cursor = constraint_arguments.walk();
21145    let constraint_types = constraint_arguments
21146        .named_children(&mut argument_cursor)
21147        .collect::<Vec<_>>();
21148    if constraint.kind() != "qualified_identifier"
21149        || constraint_template.kind() != "template_type"
21150        || !matches!(constraint_types.as_slice(), [left, right]
21151            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21152        || constraint_arguments.has_error()
21153        || node
21154            .child_by_field_name("body")
21155            .is_none_or(|candidate| !same_node(candidate, body))
21156    {
21157        return None;
21158    }
21159
21160    let mut cursor = node.walk();
21161    let recovery_errors = node
21162        .named_children(&mut cursor)
21163        .filter(|child| child.kind() == "ERROR")
21164        .collect::<Vec<_>>();
21165    if !recovery_errors
21166        .iter()
21167        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21168        || !recovery_errors.iter().all(|error| {
21169            error.named_child_count() == 0
21170                || cpp_reparsed_constraint_macro_error(*error, source)
21171                || (error.named_child_count() == 1
21172                    && error
21173                        .named_child(0)
21174                        .is_some_and(|child| child.kind() == "function_declarator"))
21175        })
21176    {
21177        return None;
21178    }
21179
21180    let parameter_text = node_text(parameter_name, source);
21181    let mut declarators = node
21182        .child_by_field_name("declarator")
21183        .and_then(extract_function_declarator)
21184        .into_iter()
21185        .collect::<Vec<_>>();
21186    for error in recovery_errors {
21187        let mut stack = vec![error];
21188        while let Some(current) = stack.pop() {
21189            if current.kind() == "function_declarator" {
21190                declarators.push(current);
21191            }
21192            let mut cursor = current.walk();
21193            stack.extend(current.named_children(&mut cursor));
21194        }
21195    }
21196    declarators.into_iter().find(|declarator| {
21197        cpp_function_declarator_name_node(*declarator)
21198            .is_some_and(|name| name.kind() == "identifier")
21199            && declarator
21200                .child_by_field_name("parameters")
21201                .is_some_and(|parameters| {
21202                    parameters
21203                        .named_children(&mut parameters.walk())
21204                        .filter_map(|parameter| parameter.child_by_field_name("type"))
21205                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21206                })
21207    })
21208}
21209
21210fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21211    node: Node<'_>,
21212    parameter_name: Node<'_>,
21213    source: &str,
21214) -> bool {
21215    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21216}
21217
21218fn cpp_reparsed_template_macro_function_companion_is_indexable(
21219    node: Node<'_>,
21220    parameter_name: Node<'_>,
21221    source: &str,
21222) -> bool {
21223    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21224        return false;
21225    }
21226    let Some(return_type) = node.child_by_field_name("type") else {
21227        return false;
21228    };
21229    let Some(function_declarator) = node
21230        .child_by_field_name("declarator")
21231        .and_then(extract_function_declarator)
21232    else {
21233        return false;
21234    };
21235    if cpp_function_declarator_name_node(function_declarator).is_none()
21236        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21237    {
21238        return false;
21239    }
21240    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21241        return false;
21242    };
21243    let parameter_text = node_text(parameter_name, source);
21244    parameters
21245        .named_children(&mut parameters.walk())
21246        .any(|parameter| {
21247            parameter
21248                .child_by_field_name("type")
21249                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21250        })
21251}
21252
21253fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21254    if node.kind() != "ERROR" {
21255        return false;
21256    }
21257    let mut stack = vec![node];
21258    while let Some(current) = stack.pop() {
21259        let macro_shape = match current.kind() {
21260            "call_expression" => current
21261                .child_by_field_name("function")
21262                .zip(current.child_by_field_name("arguments")),
21263            "init_declarator" => current
21264                .child_by_field_name("declarator")
21265                .zip(current.child_by_field_name("value")),
21266            _ => None,
21267        };
21268        if let Some((name, arguments)) = macro_shape
21269            && name.kind() == "identifier"
21270            && arguments.kind() == "argument_list"
21271            && arguments.named_child_count() >= 2
21272            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21273        {
21274            return true;
21275        }
21276        let mut cursor = current.walk();
21277        stack.extend(current.named_children(&mut cursor));
21278    }
21279    false
21280}
21281
21282fn cpp_recovered_template_macro_constructor<'tree>(
21283    node: Node<'tree>,
21284    source: &str,
21285) -> Option<(Node<'tree>, Node<'tree>)> {
21286    let mut prefix = node.prev_named_sibling()?;
21287    while prefix.kind() == "comment" {
21288        prefix = prefix.prev_named_sibling()?;
21289    }
21290    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21291    let parameter = parameter_name
21292        .parent()
21293        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21294    let declarator =
21295        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21296    Some((declarator, parameter))
21297}
21298
21299/// The template parameter a split constraint-macro head declares for the
21300/// member function after it, when the recovery gate admits the pair.
21301/// `template <class U, REQUIRES("...", std::is_convertible<U, int>)>` leaves
21302/// the head as an `ERROR` beside a clean function definition; the parameter is
21303/// the head's structured `type_parameter_declaration`.
21304fn cpp_recovered_template_macro_function_parameter<'tree>(
21305    node: Node<'tree>,
21306    source: &str,
21307) -> Option<Node<'tree>> {
21308    let prefix = cpp_prev_non_comment_named_sibling(node)?;
21309    let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21310    if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21311        return None;
21312    }
21313    parameter_name
21314        .parent()
21315        .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21316}
21317
21318fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21319    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21320        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21321            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21322                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21323                    function,
21324                    parameter_name,
21325                    source,
21326                )
21327        });
21328    }
21329    let Some(parameter_name) =
21330        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21331    else {
21332        return false;
21333    };
21334    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21335        cpp_reparsed_template_macro_function_companion_is_indexable(
21336            function,
21337            parameter_name,
21338            source,
21339        )
21340    })
21341}
21342
21343fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21344    let function_name = node
21345        .child_by_field_name("declarator")
21346        .and_then(extract_function_declarator)
21347        .and_then(cpp_function_declarator_name_node);
21348    if let Some(body) = cpp_reparsed_member_function_body(node)
21349        && function_name.is_some()
21350        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21351    {
21352        return true;
21353    }
21354    cpp_reparsed_attribute_member_function(node, source)
21355        || cpp_reparsed_friend_function_is_indexable(node, source)
21356        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21357        || cpp_reparsed_access_template_function_is_indexable(node, source)
21358        || cpp_recovered_template_macro_constructor(node, source).is_some()
21359}
21360
21361/// Recognize the three top-level nodes produced when an unknown attribute
21362/// macro separates an inline member's declarator from its body in a reparsed
21363/// class interior: an errorful declaration with a missing semicolon, the macro
21364/// call expression, and the complete compound body. Their adjacency and exact
21365/// structured shapes prove one recoverable member envelope; arbitrary calls or
21366/// blocks do not pass this gate.
21367fn cpp_reparsed_macro_attribute_member_sequence(
21368    children: &[Node<'_>],
21369    index: usize,
21370    source: &str,
21371) -> bool {
21372    let Some(prefix) = children.get(index).copied() else {
21373        return false;
21374    };
21375    let declaration = if prefix.kind() == "labeled_statement" {
21376        prefix
21377            .named_child(prefix.named_child_count().saturating_sub(1))
21378            .filter(|child| child.kind() == "declaration")
21379    } else {
21380        (prefix.kind() == "declaration").then_some(prefix)
21381    };
21382    let Some(declaration) = declaration else {
21383        return false;
21384    };
21385    if !declaration.has_error()
21386        || declaration
21387            .child_by_field_name("declarator")
21388            .and_then(extract_function_declarator)
21389            .and_then(cpp_function_declarator_name_node)
21390            .is_none()
21391    {
21392        return false;
21393    }
21394    let Some(attribute_statement) = children.get(index + 1).copied() else {
21395        return false;
21396    };
21397    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21398        .then(|| attribute_statement.named_child(0))
21399        .flatten()
21400        .filter(|child| child.kind() == "call_expression")
21401    else {
21402        return false;
21403    };
21404    let Some(attribute_name) = attribute_call
21405        .child_by_field_name("function")
21406        .filter(|function| function.kind() == "identifier")
21407        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21408    else {
21409        return false;
21410    };
21411    if !cpp_export_macro_token(&attribute_name) {
21412        return false;
21413    }
21414    let Some(body) = children.get(index + 2).copied() else {
21415        return false;
21416    };
21417    body.kind() == "compound_statement"
21418        && body.child(0).is_some_and(|open| open.kind() == "{")
21419        && body
21420            .child(body.child_count().saturating_sub(1))
21421            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21422        && declaration.end_byte() <= attribute_statement.start_byte()
21423        && attribute_statement.end_byte() <= body.start_byte()
21424}
21425
21426/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
21427/// types and specifiers followed by a declarator, over and over, with nothing
21428/// left over. A string-argument attribute macro is what strands them there, and
21429/// the walk indexes exactly what this reads, so admitting the region is safe
21430/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
21431/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
21432/// the whole class body.
21433fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21434    if node.kind() != "ERROR" {
21435        return false;
21436    }
21437    let run = stranded_declaration_run(node, source);
21438    run.complete && !run.declarations.is_empty()
21439}
21440
21441/// The ABI 15 grammar reads a defaulted zero-argument constructor in a
21442/// translation-unit-shaped class-body reparse as an assignment expression:
21443/// `Ctor() = default`. Admit only the exact assignment shape, and only when
21444/// the same reparse retains a separate constructor declarator for that name.
21445/// The second condition keeps an arbitrary expression statement from becoming
21446/// a member merely because it contains a callee followed by `default`.
21447/// A constructor declaration a statement-context class-body reparse reads as
21448/// a bare call statement (`value(pn_data_t* d);`). At statement scope a
21449/// constructor declaration is indistinguishable from a call, but a direct
21450/// child of the reparse root whose callee names the class can only be the
21451/// constructor: a member with no return type is nothing else.
21452fn cpp_reparsed_bare_constructor_call<'tree>(
21453    node: Node<'tree>,
21454    class_name: &str,
21455    source: &str,
21456) -> Option<Node<'tree>> {
21457    if node.kind() != "expression_statement" {
21458        return None;
21459    }
21460    let call = node
21461        .named_child(0)
21462        .filter(|child| child.kind() == "call_expression")?;
21463    let function = call.child_by_field_name("function")?;
21464    (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21465        .then_some(call)
21466}
21467
21468fn cpp_reparsed_defaulted_constructor_is_indexable(
21469    node: Node<'_>,
21470    members: &[Node<'_>],
21471    class_name: Option<&str>,
21472    source: &str,
21473) -> bool {
21474    let Some(class_name) = class_name else {
21475        return false;
21476    };
21477    if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21478        return false;
21479    }
21480    let Some(assignment) = node.named_child(0) else {
21481        return false;
21482    };
21483    if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21484        return false;
21485    }
21486    if assignment
21487        .child(1)
21488        .is_none_or(|operator| operator.kind() != "=")
21489    {
21490        return false;
21491    }
21492    let Some(call) = assignment.child_by_field_name("left") else {
21493        return false;
21494    };
21495    let Some(default_value) = assignment.child_by_field_name("right") else {
21496        return false;
21497    };
21498    let Some(function) = call.child_by_field_name("function") else {
21499        return false;
21500    };
21501    let Some(arguments) = call.child_by_field_name("arguments") else {
21502        return false;
21503    };
21504    if call.kind() != "call_expression"
21505        || function.kind() != "identifier"
21506        || arguments.kind() != "argument_list"
21507        || arguments.named_child_count() != 0
21508        || default_value.kind() != "identifier"
21509        || node_text(default_value, source) != "default"
21510        || node_text(function, source) != class_name
21511    {
21512        return false;
21513    }
21514    members.iter().any(|member| {
21515        if same_node(*member, node) {
21516            return false;
21517        }
21518        cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21519    })
21520}
21521
21522/// Return the constructor name from one direct class-member-shaped reparse
21523/// item. The function-declarator in an `ERROR` is admitted only through the
21524/// existing ERROR-only member proof; clean function definitions must carry no
21525/// return type and must retain either a body or ABI 15's `default_method_clause`.
21526fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21527    let function_declarator = match node.kind() {
21528        "function_definition"
21529            if !node.has_error() && node.child_by_field_name("type").is_none() =>
21530        {
21531            let has_body = node
21532                .child_by_field_name("body")
21533                .is_some_and(|body| body.kind() == "compound_statement");
21534            let has_default_method_clause = node
21535                .named_children(&mut node.walk())
21536                .any(|child| child.kind() == "default_method_clause");
21537            if !has_body && !has_default_method_clause {
21538                return None;
21539            }
21540            node.child_by_field_name("declarator")
21541                .and_then(extract_function_declarator)
21542        }
21543        "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21544            let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21545            let mut candidate = None;
21546            while let Some(current) = stack.pop() {
21547                if current.kind() == "function_declarator" {
21548                    if candidate.is_some() {
21549                        return None;
21550                    }
21551                    candidate = Some(current);
21552                    continue;
21553                }
21554                if current.kind() == "ERROR" {
21555                    let mut cursor = current.walk();
21556                    stack.extend(current.named_children(&mut cursor));
21557                }
21558            }
21559            candidate
21560        }
21561        _ => None,
21562    }?;
21563    cpp_function_declarator_name_node(function_declarator)
21564        .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21565}
21566
21567fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21568    cpp_reparsed_members_are_indexable_for_class(root, source, None)
21569}
21570
21571fn cpp_reparsed_members_are_indexable_for_class(
21572    root: Node<'_>,
21573    source: &str,
21574    class_name: Option<&str>,
21575) -> bool {
21576    let mut saw_member = false;
21577    let mut regions = Vec::new();
21578    let mut region_tree: Option<Tree> = None;
21579    loop {
21580        let root = region_tree.as_ref().map_or(root, Tree::root_node);
21581        let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
21582        let mut index = 0;
21583        while index < children.len() {
21584            let child = children[index];
21585            if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21586                saw_member = true;
21587                index += 3;
21588                continue;
21589            }
21590            if let Some(recovered) = fragmented_class_body(child, source) {
21591                let Some(tree) = cpp_reparse_fragmented_class_body(
21592                    source,
21593                    recovered.body.reparse_start,
21594                    recovered.body.reparse_end,
21595                ) else {
21596                    return false;
21597                };
21598                if !cpp_reparsed_members_are_indexable_for_class(
21599                    tree.root_node(),
21600                    source,
21601                    Some(recovered.name.as_str()),
21602                ) {
21603                    return false;
21604                }
21605                saw_member = true;
21606                index += 1;
21607                while index < children.len()
21608                    && children[index].end_byte() <= recovered.body.class_range.end_byte
21609                {
21610                    index += 1;
21611                }
21612                continue;
21613            }
21614            match child.kind() {
21615                "comment" => {}
21616                "labeled_statement" => {
21617                    let Some(label) = child.child_by_field_name("label") else {
21618                        return false;
21619                    };
21620                    if !matches!(node_text(label, source), "public" | "protected" | "private") {
21621                        return false;
21622                    }
21623                    let payload = child
21624                        .named_children(&mut child.walk())
21625                        .filter(|node| node.id() != label.id() && node.kind() != "comment")
21626                        .collect::<Vec<_>>();
21627                    if payload.is_empty() {
21628                        return false;
21629                    }
21630                    // The statement-context grammar splits these constructors
21631                    // across an ERROR and initializer-looking declarations.
21632                    if class_name.is_some_and(|name| {
21633                        cpp_reparsed_synthetic_initializer_constructor(
21634                            child,
21635                            name,
21636                            source,
21637                            child.end_byte(),
21638                        )
21639                        .is_some()
21640                            || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
21641                    }) {
21642                        saw_member = true;
21643                        index += 1;
21644                        continue;
21645                    }
21646                    // A destructor in statement context is a unary '~' call,
21647                    // with a possible stranded virtual keyword ahead of it.
21648                    let destructor = match payload.as_slice() {
21649                        [expression] => Some(*expression),
21650                        [specifier, expression]
21651                            if specifier.kind() == "ERROR"
21652                                && specifier.child_count() == 1
21653                                && specifier
21654                                    .child(0)
21655                                    .is_some_and(|keyword| keyword.kind() == "virtual") =>
21656                        {
21657                            Some(*expression)
21658                        }
21659                        _ => None,
21660                    };
21661                    if destructor.is_some_and(|expression| {
21662                        expression.kind() == "expression_statement"
21663                            && !expression.has_error()
21664                            && expression.named_child(0).is_some_and(|unary| {
21665                                unary.kind() == "unary_expression"
21666                                    && unary
21667                                        .child_by_field_name("operator")
21668                                        .is_some_and(|operator| operator.kind() == "~")
21669                                    && unary.child_by_field_name("argument").is_some_and(|call| {
21670                                        call.kind() == "call_expression"
21671                                            && call.child_by_field_name("function").is_some_and(
21672                                                |name| {
21673                                                    name.kind() == "identifier"
21674                                                        && class_name.is_none_or(|owner| {
21675                                                            node_text(name, source) == owner
21676                                                        })
21677                                                },
21678                                            )
21679                                            && call.child_by_field_name("arguments").is_some_and(
21680                                                |arguments| arguments.named_child_count() == 0,
21681                                            )
21682                                    })
21683                            })
21684                    }) {
21685                        saw_member = true;
21686                    } else {
21687                        // A label puts its payload in statement context, where
21688                        // static assertions look like calls and alias templates
21689                        // can become ERROR nodes. Reparse after the colon as
21690                        // declarations and apply this same gate to that region.
21691                        let colon = child
21692                            .children(&mut child.walk())
21693                            .find(|node| node.kind() == ":")
21694                            .expect("an access label has a colon");
21695                        regions.push(colon.end_byte()..child.end_byte());
21696                    }
21697                }
21698                "function_definition" => {
21699                    if child.has_error()
21700                        && !cpp_reparsed_member_function_is_indexable(child, source)
21701                        && cpp_sentinel_macro_region(child, source).is_none()
21702                    {
21703                        return false;
21704                    }
21705                    saw_member = true;
21706                }
21707                // The attribute a string-argument macro leaves as a call statement
21708                // of its own. It declares nothing; the member it decorated is the
21709                // sibling after it, checked on its own turn.
21710                "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
21711                "expression_statement"
21712                    if cpp_reparsed_defaulted_constructor_is_indexable(
21713                        child, &children, class_name, source,
21714                    ) =>
21715                {
21716                    saw_member = true;
21717                }
21718                "expression_statement"
21719                    if class_name.is_some_and(|class_name| {
21720                        cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21721                    }) =>
21722                {
21723                    saw_member = true;
21724                }
21725                "ERROR"
21726                    if (cpp_reparsed_member_error_is_indexable(child)
21727                        || cpp_reparsed_adjacent_copy_control_error(child, source)
21728                        || cpp_reparsed_stranded_member_error(child, source))
21729                        && (child
21730                        .next_named_sibling()
21731                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21732                        || cpp_reparsed_member_error_with_preprocessed_body(child)
21733                        // The macro-declaration grammar folds the member after
21734                        // the copy-control pair into the same ERROR and leaves
21735                        // its body as the following sibling.
21736                        || (cpp_reparsed_adjacent_copy_control_error(child, source)
21737                            && child
21738                                .next_named_sibling()
21739                                .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21740                {
21741                    saw_member = true;
21742                }
21743                "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21744                    saw_member = true;
21745                }
21746                "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21747                    saw_member = true;
21748                }
21749                "expression_statement"
21750                    if cpp_is_stray_semicolon(child, source)
21751                        && child.prev_named_sibling().is_some_and(|error| {
21752                            cpp_reparsed_member_error_is_indexable(error)
21753                                || cpp_reparsed_adjacent_copy_control_error(error, source)
21754                                || cpp_reparsed_stranded_member_error(error, source)
21755                        }) =>
21756                {
21757                    saw_member = true;
21758                }
21759                "compound_statement"
21760                    if cpp_reparsed_constructor_body_is_indexable(child, source)
21761                        || cpp_reparsed_attribute_requires_body(child, source)
21762                        || child.prev_named_sibling().is_some_and(|error| {
21763                            cpp_reparsed_adjacent_copy_control_error(error, source)
21764                        }) =>
21765                {
21766                    saw_member = true;
21767                }
21768                // A copy-control declaration the macro-declaration grammar reads
21769                // as a function-like macro wrapping a type: `XMLElement( const
21770                // XMLElement& )` in a statement-context reparse is a
21771                // `macro_type_specifier`, admitted only where the parse would
21772                // otherwise contain an error. The live `;` beside it is the
21773                // declaration's own terminator, the same proof the ERROR
21774                // envelope's stray semicolon carried.
21775                "macro_type_specifier"
21776                    if child
21777                        .child_by_field_name("name")
21778                        .is_some_and(|name| name.kind() == "identifier")
21779                        && child
21780                            .child_by_field_name("type")
21781                            .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21782                        && child.next_sibling().is_some_and(|sibling| {
21783                            sibling.kind() == ";" && !sibling.is_missing()
21784                        }) =>
21785                {
21786                    saw_member = true;
21787                }
21788                kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21789                _ => return false,
21790            }
21791            index += 1;
21792        }
21793        let Some(region) = regions.pop() else {
21794            return saw_member;
21795        };
21796        let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
21797            return false;
21798        };
21799        region_tree = Some(tree);
21800    }
21801}
21802
21803/// Detect the malformed constructor shape that tree-sitter exposes as an
21804/// access-label statement followed by initializer-looking declarations. The
21805/// declarations are not class members: visiting their `location(loc)` and
21806/// `string(s)` function declarators would publish synthetic functions. The
21807/// export-class fallback keeps the original sibling nodes and therefore avoids
21808/// this parser artifact. The returned range identifies the real constructor
21809/// header, which can be reparsed independently as a structured declarator.
21810fn cpp_reparsed_synthetic_initializer_constructor_range(
21811    root: Node<'_>,
21812    class_name: &str,
21813    source: &str,
21814    constructor_end: usize,
21815) -> Option<std::ops::Range<usize>> {
21816    let mut stack = {
21817        let mut cursor = root.walk();
21818        root.named_children(&mut cursor).collect::<Vec<_>>()
21819    };
21820    while let Some(current) = stack.pop() {
21821        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21822            current,
21823            class_name,
21824            source,
21825            constructor_end,
21826        ) {
21827            return Some(range);
21828        }
21829        if current.kind() == "ERROR" {
21830            let mut cursor = current.walk();
21831            stack.extend(current.named_children(&mut cursor));
21832        }
21833    }
21834    None
21835}
21836
21837/// Recover an inline constructor that a function-like export macro makes
21838/// tree-sitter merge with the following overload. In the reparsed class-body
21839/// region, the access label wraps one declaration whose ERROR contains the
21840/// constructor declarator and its base-initializer/body, while the declaration's
21841/// ordinary declarator is the following overload. Every boundary below comes
21842/// from that CST; no source syntax is reparsed by hand.
21843fn cpp_reparsed_merged_inline_constructor<'tree>(
21844    root: Node<'tree>,
21845    class_name: &str,
21846    source: &str,
21847) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21848    let mut stack = vec![root];
21849    while let Some(current) = stack.pop() {
21850        if current.kind() != "labeled_statement" {
21851            let mut cursor = current.walk();
21852            stack.extend(current.named_children(&mut cursor));
21853            continue;
21854        }
21855        let Some(declaration) = current
21856            .named_children(&mut current.walk())
21857            .find(|child| child.kind() == "declaration")
21858        else {
21859            continue;
21860        };
21861        if declaration
21862            .child_by_field_name("type")
21863            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21864        {
21865            continue;
21866        }
21867        let following = declaration
21868            .child_by_field_name("declarator")
21869            .and_then(extract_function_declarator)
21870            .and_then(cpp_function_declarator_name_node);
21871        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21872            continue;
21873        }
21874        let mut declaration_cursor = declaration.walk();
21875        let Some(error) = declaration
21876            .named_children(&mut declaration_cursor)
21877            .find(|child| child.kind() == "ERROR")
21878        else {
21879            continue;
21880        };
21881        let mut error_cursor = error.walk();
21882        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21883        let Some(constructor) = error_children.iter().copied().find(|child| {
21884            child.kind() == "function_declarator"
21885                && cpp_function_declarator_name_node(*child)
21886                    .is_some_and(|name| node_text(name, source).trim() == class_name)
21887        }) else {
21888            continue;
21889        };
21890        let Some(body) = error_children.iter().copied().find_map(|child| {
21891            (child.kind() == "init_declarator")
21892                .then(|| child.child_by_field_name("value"))
21893                .flatten()
21894                .filter(|value| value.kind() == "initializer_list")
21895        }) else {
21896            continue;
21897        };
21898        if constructor.end_byte() > body.start_byte() {
21899            continue;
21900        }
21901        return Some((constructor.start_byte()..body.end_byte(), body));
21902    }
21903    None
21904}
21905
21906fn cpp_reparsed_synthetic_initializer_constructor(
21907    node: Node<'_>,
21908    class_name: &str,
21909    source: &str,
21910    constructor_end: usize,
21911) -> Option<std::ops::Range<usize>> {
21912    if node.kind() != "labeled_statement" {
21913        return None;
21914    }
21915    let mut cursor = node.walk();
21916    let named = node
21917        .named_children(&mut cursor)
21918        .filter(|child| child.kind() != "comment")
21919        .collect::<Vec<_>>();
21920    let label = named.first()?;
21921    if label.kind() != "statement_identifier"
21922        || !matches!(
21923            node_text(*label, source).trim(),
21924            "public" | "private" | "protected"
21925        )
21926    {
21927        return None;
21928    }
21929    let call_error_index = named.iter().position(|child| {
21930        if child.kind() != "ERROR" {
21931            return false;
21932        }
21933        // The macro-declaration grammar keeps the constructor head's type as
21934        // the ERROR's own type field; the older grammar flattened the head
21935        // into a call expression inside it. ERROR nodes retain field labels
21936        // only by child index in runtime 0.25.
21937        let type_child = (0..child.child_count()).find_map(|index| {
21938            let candidate = child.child(index)?;
21939            (child.field_name_for_child(index as u32) == Some("type")
21940                && candidate.kind() == "type_identifier"
21941                && node_text(candidate, source).trim() == class_name)
21942                .then_some(candidate)
21943        });
21944        if type_child.is_some() {
21945            return true;
21946        }
21947        let mut stack = vec![*child];
21948        while let Some(current) = stack.pop() {
21949            if current.kind() == "call_expression"
21950                && current
21951                    .child_by_field_name("function")
21952                    .is_some_and(|function| {
21953                        function.kind() == "identifier"
21954                            && node_text(function, source).trim() == class_name
21955                    })
21956            {
21957                return true;
21958            }
21959            let mut cursor = current.walk();
21960            stack.extend(current.named_children(&mut cursor));
21961        }
21962        false
21963    })?;
21964    let constructor_start = {
21965        let head = named[call_error_index];
21966        let type_child = (0..head.child_count()).find_map(|index| {
21967            let candidate = head.child(index)?;
21968            (head.field_name_for_child(index as u32) == Some("type")
21969                && candidate.kind() == "type_identifier"
21970                && node_text(candidate, source).trim() == class_name)
21971                .then_some(candidate)
21972        });
21973        if let Some(type_node) = type_child {
21974            type_node.start_byte()
21975        } else {
21976            let mut stack = vec![head];
21977            let mut found = None;
21978            while let Some(current) = stack.pop() {
21979                if current.kind() == "call_expression"
21980                    && current
21981                        .child_by_field_name("function")
21982                        .is_some_and(|function| {
21983                            function.kind() == "identifier"
21984                                && node_text(function, source).trim() == class_name
21985                        })
21986                {
21987                    found = Some(current);
21988                    break;
21989                }
21990                let mut cursor = current.walk();
21991                stack.extend(current.named_children(&mut cursor));
21992            }
21993            found?.start_byte()
21994        }
21995    };
21996    named.iter().skip(call_error_index + 1).find(|child| {
21997        child.kind() == "declaration" && child.has_error() && {
21998            let mut cursor = child.walk();
21999            child.named_children(&mut cursor).any(|declarator| {
22000                declarator.kind() == "init_declarator"
22001                    && declarator
22002                        .child_by_field_name("declarator")
22003                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
22004                    && declarator
22005                        .child_by_field_name("value")
22006                        .is_some_and(|value| value.kind() == "initializer_list")
22007            })
22008        }
22009    })?;
22010    Some(constructor_start..constructor_end)
22011}
22012
22013fn cpp_reparsed_exact_constructor_declarator<'tree>(
22014    root: Node<'tree>,
22015    start: usize,
22016    class_name: &str,
22017    source: &str,
22018) -> Option<Node<'tree>> {
22019    let mut candidate = None;
22020    let mut stack = vec![root];
22021    while let Some(current) = stack.pop() {
22022        if current.kind() == "function_declarator"
22023            && current.start_byte() == start
22024            && cpp_function_declarator_name_node(current)
22025                .is_some_and(|name| node_text(name, source).trim() == class_name)
22026        {
22027            if candidate.is_some() {
22028                return None;
22029            }
22030            candidate = Some(current);
22031            continue;
22032        }
22033        let mut cursor = current.walk();
22034        stack.extend(current.named_children(&mut cursor));
22035    }
22036    candidate
22037}
22038
22039fn cpp_is_indexable_item_kind(kind: &str) -> bool {
22040    matches!(
22041        kind,
22042        "namespace_definition"
22043            | "class_specifier"
22044            | "struct_specifier"
22045            | "union_specifier"
22046            | "enum_specifier"
22047            | "function_definition"
22048            | "template_declaration"
22049            | "declaration"
22050            | "field_declaration"
22051            | "alias_declaration"
22052            | "static_assert_declaration"
22053            | "type_definition"
22054            | "using_declaration"
22055            | "linkage_specification"
22056            | "preproc_def"
22057            | "preproc_function_def"
22058            | "preproc_include"
22059            | "preproc_if"
22060            | "preproc_ifdef"
22061            | "preproc_call"
22062    )
22063}
22064
22065#[cfg(test)]
22066mod tests {
22067
22068    #[test]
22069    fn recovered_callable_grammar_and_original_bytes() {
22070        use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
22071
22072        let parsed = tree(SOURCE);
22073        // These are the current grammar's contracts, including the macro_modifier
22074        // support that has replaced some older ERROR/argument_list recoveries.
22075        // Each row selects a real source region and independently pins its fields
22076        // and the tokens that must survive a synthetic class-context reparse.
22077        let cases = [
22078            (
22079                "expression_statement",
22080                58..72,
22081                &[][..],
22082                &[(58..64, "Widget"), (65..70, "value")][..],
22083            ),
22084            (
22085                "field_declaration",
22086                92..145,
22087                &[
22088                    ("type", "qualified_identifier", 111..122),
22089                    ("declarator", "function_declarator", 123..144),
22090                ][..],
22091                &[
22092                    (123..131, "toString"),
22093                    (132..137, "Yield"),
22094                    (138..143, "yield"),
22095                ][..],
22096            ),
22097            (
22098                "function_definition",
22099                147..191,
22100                &[
22101                    ("type", "qualified_identifier", 151..161),
22102                    ("declarator", "parenthesized_declarator", 161..174),
22103                    ("body", "compound_statement", 189..191),
22104                ][..],
22105                &[
22106                    (155..161, "Widget"),
22107                    (162..167, "Value"),
22108                    (168..173, "value"),
22109                ][..],
22110            ),
22111            (
22112                "field_declaration",
22113                193..296,
22114                &[
22115                    ("type", "macro_type_specifier", 193..222),
22116                    ("declarator", "macro_modifier_declarator", 223..263),
22117                    ("default_value", "initializer_list", 291..293),
22118                ][..],
22119                &[
22120                    (233..239, "Widget"),
22121                    (240..248, "grow_fun"),
22122                    (249..253, "grow"),
22123                ][..],
22124            ),
22125            (
22126                "call_expression",
22127                58..71,
22128                &[
22129                    ("function", "identifier", 58..64),
22130                    ("arguments", "argument_list", 64..71),
22131                ][..],
22132                &[][..],
22133            ),
22134            (
22135                "function_declarator",
22136                123..144,
22137                &[
22138                    ("declarator", "field_identifier", 123..131),
22139                    ("parameters", "parameter_list", 131..144),
22140                ][..],
22141                &[][..],
22142            ),
22143            (
22144                "function_declarator",
22145                233..263,
22146                &[
22147                    ("declarator", "field_identifier", 233..239),
22148                    ("parameters", "parameter_list", 239..254),
22149                ][..],
22150                &[][..],
22151            ),
22152        ];
22153        for (kind, span, fields, tokens) in cases {
22154            let node = at(parsed.root_node(), kind, span.clone());
22155            for (name, child_kind, child_span) in fields {
22156                field(node, name, child_kind, child_span.clone());
22157            }
22158            if tokens.is_empty() {
22159                continue;
22160            }
22161            // Match visit_bare_constructor_expression's documented class context.
22162            // The initializer-list body bounds the initialized-constructor region;
22163            // its recovery field also swallowed the original class close.
22164            let end = node
22165                .child_by_field_name("default_value")
22166                .map_or(span.end, |body| body.end_byte());
22167            const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22168            let parsed = crate::graph::syntax::parse_sentinel(
22169                PREFIX,
22170                crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22171                " };",
22172            );
22173            let (synthetic, reparsed) = (parsed.source, parsed.tree);
22174            for (original_span, text) in tokens {
22175                assert_eq!(&SOURCE[original_span.clone()], *text);
22176                let translated = PREFIX.len() + original_span.start - span.start
22177                    ..PREFIX.len() + original_span.end - span.start;
22178                let leaves: Vec<_> = nodes(reparsed.root_node())
22179                    .into_iter()
22180                    .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22181                    .collect();
22182                assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22183                let token = leaves[0];
22184                assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22185                assert_eq!(
22186                    token.start_byte() - PREFIX.len() + span.start
22187                        ..token.end_byte() - PREFIX.len() + span.start,
22188                    original_span.clone()
22189                );
22190                assert!(SOURCE.is_char_boundary(original_span.start));
22191                assert!(SOURCE.is_char_boundary(original_span.end));
22192            }
22193        }
22194    }
22195
22196    use super::*;
22197    use crate::adapter::parse_cpp_file;
22198    use brokk_bifrost_core::analyzer::parsed_file::{
22199        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22200    };
22201    use brokk_bifrost_test_macros::hardwon;
22202    use std::fmt::Write;
22203
22204    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22205        let tree = crate::diagnostics::parse_cpp_tree(source);
22206        let file = ProjectFile::new(std::env::temp_dir(), name);
22207        parse_cpp_file(&file, source, &tree)
22208    }
22209
22210    #[test]
22211    #[hardwon(
22212        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22213    )]
22214    fn comment_only_enums_have_no_enumerators() {
22215        let parsed =
22216            parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22217        let names: Vec<_> = parsed
22218            .declarations()
22219            .iter()
22220            .map(|unit| unit.fq_name())
22221            .collect();
22222        assert_eq!(names, ["Empty"]);
22223    }
22224
22225    #[test]
22226    #[hardwon(
22227        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22228    )]
22229    fn quicksilver_gnu_attribute_orders() {
22230        for attributes in [
22231            "deprecated, warn_unused_result",
22232            "warn_unused_result, deprecated",
22233        ] {
22234            let source = format!("int value() __attribute__(({attributes}));");
22235            let tree = crate::diagnostics::parse_cpp_tree(&source);
22236            assert!(
22237                !tree.root_node().has_error(),
22238                "{}",
22239                tree.root_node().to_sexp()
22240            );
22241            let declaration = tree.root_node().named_child(0).unwrap();
22242            assert!(
22243                cpp_declaration_has_native_must_use_attribute(declaration, &source),
22244                "{source}: {}",
22245                declaration.to_sexp()
22246            );
22247        }
22248    }
22249
22250    #[test]
22251    #[hardwon(
22252        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22253    )]
22254    fn fragmented_brace_matching_ignores_literal_tokens() {
22255        // Isolate the brace proof from export-head recognition: the damaged
22256        // field list supplies the same container boundary used by recovery.
22257        let source =
22258            r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22259        let tree = crate::diagnostics::parse_cpp_tree(source);
22260        let tokens = crate::grammar_test_support::nodes(tree.root_node());
22261        assert!(
22262            tokens
22263                .iter()
22264                .any(|node| node.kind() == "raw_string_literal")
22265        );
22266        let body = tokens
22267            .iter()
22268            .find(|node| node.kind() == "field_declaration_list")
22269            .unwrap();
22270        let opening = body.child(0).unwrap();
22271        let head = body.named_child(0).unwrap();
22272        assert_eq!(opening.kind(), "{");
22273        assert!(body.has_error());
22274        assert_eq!(
22275            fragmented_container_close_class_body(head, opening, source)
22276                .map(|body| body.reparse_end),
22277            source.rfind('}')
22278        );
22279    }
22280
22281    #[test]
22282    #[hardwon(
22283        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22284    )]
22285    fn quicksilver_uppercase_attributed_classes() {
22286        for (name, body) in [
22287            ("Box", "{ int value; }"),
22288            ("BOX", "{ int value; }"),
22289            ("API", ""),
22290        ] {
22291            let source = format!("class [[deprecated]] {name} {body};");
22292            let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22293            let names = parsed
22294                .declarations()
22295                .iter()
22296                .filter(|unit| unit.is_class())
22297                .map(|unit| unit.identifier().to_owned())
22298                .collect::<Vec<_>>();
22299            assert_eq!(names, [name], "{source}");
22300        }
22301    }
22302
22303    #[test]
22304    #[hardwon(
22305        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22306    )]
22307    fn quicksilver_fragmented_export_uppercase_classes() {
22308        for name in ["Box", "BOX"] {
22309            let source = r#"
22310namespace api {
22311
22312/**
22313* Doc comment
22314*/
22315class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22316   public:
22317      /**
22318      * Construct from a point.
22319      */
22320      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22321
22322#if defined(PROJECT_HAS_LEGACY_POINT)
22323      /**
22324      * Construct from a legacy point.
22325      */
22326      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22327#endif
22328
22329      std::string algo_name() const override;
22330
22331      AlgorithmIdentifier algorithm_identifier() const override;
22332};
22333
22334}
22335"#
22336            .replace("VirtualBased", name);
22337            // Positional recovery once named this class. Since
22338            // brokk-tree-sitter-cpp 0.24.7 the grammar reads the head, and an
22339            // uppercase name must survive that reading too.
22340            let tree = crate::grammar_test_support::tree(&source);
22341            crate::grammar_test_support::assert_function_like_export_class_head(
22342                &tree, &source, name,
22343            );
22344            let parsed = parse_cpp_declarations(&source, "virtual-base.hpp");
22345            let explicit = parse_cpp_declarations(
22346                &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
22347                "virtual-base.hpp",
22348            );
22349            assert_eq!(
22350                declaration_expansion_inventory(&parsed),
22351                declaration_expansion_inventory(&explicit),
22352                "{name}"
22353            );
22354            let declarations = parsed.declarations();
22355            let class = declarations
22356                .iter()
22357                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22358                .unwrap_or_else(|| panic!("{name}: missing class: {declarations:#?}"));
22359            assert_eq!(
22360                parsed.raw_supertypes.get(class),
22361                Some(&vec!["BaseKey".to_string()]),
22362                "{name}"
22363            );
22364            assert!(
22365                declarations.iter().any(|unit| {
22366                    unit.is_function() && unit.fq_name() == format!("api.{name}.algo_name")
22367                }),
22368                "{name}: the class must own its members: {declarations:#?}"
22369            );
22370        }
22371    }
22372
22373    #[test]
22374    #[hardwon(
22375        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22376    )]
22377    fn quicksilver_constructor_search_skips_other_labels() {
22378        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";
22379        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22380        let (range, body) =
22381            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22382                .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22383        assert_eq!(
22384            &source[range],
22385            "Lookup_Error(std::string_view err) : Exception(err) {}"
22386        );
22387        assert_eq!(node_text(body, source), "{}");
22388    }
22389
22390    #[test]
22391    #[hardwon(
22392        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
22393    )]
22394    fn quicksilver_member_gate_checks_label_payload() {
22395        for (source, expected) in [
22396            ("public: int value;", true),
22397            (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
22398            (
22399                "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
22400                true,
22401            ),
22402            ("public: do_work();", false),
22403            ("ordinary: int value;", false),
22404        ] {
22405            let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22406            assert_eq!(
22407                cpp_reparsed_members_are_indexable(tree.root_node(), source),
22408                expected,
22409                "{source}: {}",
22410                tree.root_node().to_sexp()
22411            );
22412        }
22413    }
22414
22415    #[test]
22416    #[hardwon(
22417        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
22418    )]
22419    fn quicksilver_sentinel_uppercase_classes() {
22420        for name in ["Box", "BOX"] {
22421            let source = format!(
22422                "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"
22423            );
22424            let tree = crate::diagnostics::parse_cpp_tree(&source);
22425            let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
22426            assert!(
22427                recovered
22428                    .iter()
22429                    .any(|class| class.scope_components == ["absl", "container_internal", name]),
22430                "{source}: {recovered:#?}"
22431            );
22432        }
22433    }
22434
22435    #[test]
22436    fn native_must_use_attribute_nodes_are_structural() {
22437        let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
22438        let tree = crate::diagnostics::parse_cpp_tree(source);
22439        let root = tree.root_node();
22440        let mut declarations = Vec::new();
22441        walk_named_tree_preorder(root, true, |node| {
22442            if matches!(
22443                node.kind(),
22444                "declaration" | "function_definition" | "field_declaration"
22445            ) {
22446                declarations.push(node);
22447            }
22448            WalkControl::Continue
22449        });
22450        assert_eq!(
22451            declarations
22452                .into_iter()
22453                .map(|declaration| {
22454                    cpp_declaration_has_native_must_use_attribute(declaration, source)
22455                })
22456                .collect::<Vec<_>>(),
22457            vec![true, false, true]
22458        );
22459        let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
22460        let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
22461        let mut enum_specifier = None;
22462        walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
22463            if node.kind() == "enum_specifier" {
22464                enum_specifier = Some(node);
22465                WalkControl::Break
22466            } else {
22467                WalkControl::Continue
22468            }
22469        });
22470        assert!(cpp_declaration_has_native_must_use_attribute(
22471            enum_specifier.expect("enum declaration"),
22472            enum_source
22473        ));
22474    }
22475
22476    fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
22477        let mut rows = parsed
22478            .signature_metadata
22479            .iter()
22480            .flat_map(|(unit, entries)| {
22481                entries.iter().map(move |metadata| {
22482                    assert!(
22483                        metadata.callable_modifiers_recorded(),
22484                        "{} must record that the walk read its declaration shape",
22485                        unit.fq_name()
22486                    );
22487                    format!(
22488                        "{} decl_only={} static={} constructor={} visibility={:?}",
22489                        unit.fq_name(),
22490                        metadata.is_declaration_only(),
22491                        metadata.callable_is_static(),
22492                        metadata.callable_is_constructor(),
22493                        metadata
22494                            .callable_declared_visibility()
22495                            .expect("a recorded modifier set states a visibility"),
22496                    )
22497                })
22498            })
22499            .collect::<Vec<_>>();
22500        rows.sort();
22501        rows
22502    }
22503
22504    /// #3520: the input-edge rebuild of a rendered C++ name must render back to
22505    /// exactly the spelling it was given, with the nested-class `$` recorded as
22506    /// a [`SegmentKind::Nested`] join and a member as its own tail segment.
22507    #[test]
22508    #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
22509    fn rendered_name_fq_round_trips_nested_class_spellings() {
22510        assert!(cpp_rendered_name_fq("").is_empty());
22511        for rendered in [
22512            "Only",
22513            "Top$Only",
22514            "ns.Outer$Inner",
22515            "Top$Only.b",
22516            "ns.Outer$Inner.a",
22517            "Buck$Stop",
22518        ] {
22519            let fq = cpp_rendered_name_fq(rendered);
22520            assert_eq!(
22521                fq.display_native(Language::Cpp, segment_interner()),
22522                rendered,
22523                "native round trip for {rendered}"
22524            );
22525        }
22526        let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
22527            .segments()
22528            .iter()
22529            .map(|segment| segment_interner().resolve(*segment).1)
22530            .collect();
22531        assert_eq!(
22532            kinds,
22533            [
22534                SegmentKind::Unknown,
22535                SegmentKind::Unknown,
22536                SegmentKind::Nested
22537            ]
22538        );
22539    }
22540
22541    /// #3631: a `::`-only source spelling must name the nested `$` identity
22542    /// and the namespace `.` identity. Canonical `.` / `$` spellings stay on
22543    /// the single rendered-name parse.
22544    #[test]
22545    #[hardwon(
22546        "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
22547    )]
22548    fn colon_source_aliases_name_nested_and_namespace_identities() {
22549        let native = |rendered: &str| {
22550            cpp_colon_source_aliases(rendered)
22551                .into_iter()
22552                .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
22553                .collect::<Vec<_>>()
22554        };
22555        let top = native("Top::Only");
22556        assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
22557        assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
22558        assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
22559        let nested = native("ns::Outer::Inner");
22560        assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
22561        assert!(
22562            nested.contains(&"ns::Outer.Inner".to_string()),
22563            "{nested:?}"
22564        );
22565        let member = native("Top::Only::b");
22566        assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
22567        assert!(cpp_colon_source_aliases("Top$Only").is_empty());
22568        assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
22569        assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
22570        assert!(cpp_colon_source_aliases("Only").is_empty());
22571        assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
22572            fq.segments()
22573                .iter()
22574                .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
22575        }));
22576    }
22577
22578    #[test]
22579    #[hardwon(
22580        "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
22581    )]
22582    fn displaced_terminator_ignores_nested_initializer_endif() {
22583        let source = "#ifdef ENABLE_ITEMS\n\
22584struct Item { int reg; };\n\
22585static const struct Item items[] = {{1}};\n\
22586static const struct Item extras[] = {\n\
22587  {0},\n\
22588#ifndef REDUCED\n\
22589  {1},\n\
22590#endif\n\
22591};\n\
22592int read_item(int i) { return items[i].reg; }\n\
22593#endif\n";
22594        let mut parser = tree_sitter::Parser::new();
22595        parser
22596            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22597            .expect("C++ grammar");
22598        let tree = parser.parse(source, None).expect("fixture tree");
22599        let conditional = tree
22600            .root_node()
22601            .named_child(0)
22602            .filter(|node| node.kind() == "preproc_ifdef")
22603            .expect("outer conditional");
22604
22605        assert!(conditional.has_error());
22606        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
22607        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
22608    }
22609
22610    #[test]
22611    #[hardwon(
22612        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22613    )]
22614    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
22615        let source = "struct Image { PyObject_HEAD Imaging image; };";
22616        let parsed = parse_cpp_declarations(source, "image.h");
22617        let names = parsed
22618            .declarations()
22619            .iter()
22620            .map(|unit| unit.fq_name())
22621            .collect::<Vec<_>>();
22622
22623        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
22624        assert!(
22625            names.iter().all(|name| name != "Image.Imaging"),
22626            "the pseudo-declarator must not become a field: {names:#?}"
22627        );
22628
22629        let pointer = parse_cpp_declarations(
22630            "struct Image { PyObject_HEAD Imaging *image; };",
22631            "image-pointer.h",
22632        );
22633        let pointer_names = pointer
22634            .declarations()
22635            .iter()
22636            .map(|unit| unit.fq_name())
22637            .collect::<Vec<_>>();
22638        assert!(
22639            pointer_names.iter().any(|name| name == "Image.image"),
22640            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
22641        );
22642        assert!(
22643            pointer_names.iter().all(|name| name != "Image.Imaging"),
22644            "the pointer recovery error must not become a field: {pointer_names:#?}"
22645        );
22646
22647        let unrelated_macro = parse_cpp_declarations(
22648            "struct Image { OTHER_HEAD Imaging other; };",
22649            "image-near-miss.h",
22650        );
22651        let unrelated_names = unrelated_macro
22652            .declarations()
22653            .iter()
22654            .map(|unit| unit.fq_name())
22655            .collect::<Vec<_>>();
22656        assert!(
22657            unrelated_names.iter().any(|name| name == "Image.other"),
22658            "an object-like macro in specifier position is a `macro_modifier`, so the \
22659             member is the structured best-effort reading, the same one the \
22660             macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
22661        );
22662        assert!(
22663            unrelated_names
22664                .iter()
22665                .all(|name| name != "Image.OTHER_HEAD"),
22666            "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
22667        );
22668    }
22669
22670    #[test]
22671    #[hardwon(
22672        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22673    )]
22674    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
22675        let source = r#"namespace testing {
22676namespace internal {
22677    namespace detail {
22678        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
22679        public:
22680            int value() const { return count_ + 1; }
22681        private:
22682            int count_;
22683        };
22684        class GTEST_API_ [[nodiscard]] OtherReporter {
22685        public:
22686            int value() const { return count_ + 2; }
22687        private:
22688            int count_;
22689        };
22690    }
22691
22692    template <typename T>
22693    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
22694
22695    class TailReporter {};
22696}
22697}
22698"#;
22699        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
22700        let declarations = parsed.declarations();
22701        let mut parser = tree_sitter::Parser::new();
22702        parser
22703            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22704            .unwrap();
22705        let tree = parser.parse(source, None).unwrap();
22706        let tail_start = source.find("TailReporter").expect("tail class");
22707        let tail_node = tree
22708            .root_node()
22709            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
22710            .expect("tail class AST node");
22711        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22712        assert!(
22713            tree.root_node().has_error(),
22714            "the malformed class must exercise recovery"
22715        );
22716        assert!(index.region_at(tail_start).is_some());
22717        assert_eq!(
22718            index.enclosing_namespace_components(tail_node, source),
22719            ["testing", "internal"]
22720        );
22721        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
22722        let mut recovered_parsed = ParsedFile::new(String::new());
22723        let class_unit = CodeUnit::new_fq(
22724            file.clone(),
22725            CodeUnitType::Class,
22726            "testing",
22727            "ScopedFakeTestPartResultReporter",
22728            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
22729        );
22730        let scope = ScopeInfo {
22731            package_name: "testing".to_string(),
22732            module: None,
22733            class_unit: Some(class_unit),
22734            template_signature: Some("<typename T>".to_string()),
22735            template_metadata: Some(CppTemplateMetadata {
22736                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
22737                primary_fq_name: String::new(),
22738                parameters: Vec::new(),
22739                specialization_arguments: Vec::new(),
22740                alias_target: None,
22741            }),
22742            declarations_are_fields: true,
22743            recovered_specialization_member_scope: true,
22744            visible_using_namespaces: Vec::new(),
22745            local_block: None,
22746            visible_using_declarations: Arc::default(),
22747        };
22748        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22749        let properties = RefCell::new(HashMap::default());
22750        let dependencies =
22751            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22752        let ancestry = ParentIndex::new(tree.root_node());
22753        let mut visitor = CppVisitor {
22754            file: &file,
22755            source,
22756            parsed: &mut recovered_parsed,
22757            source_collector: &collector,
22758            source_properties: &properties,
22759            source_dependencies: &dependencies,
22760            primary_ancestry: &ancestry,
22761            primary_root_id: tree.root_node().id(),
22762            source_links: HashMap::default(),
22763            source_declarations: HashSet::default(),
22764            preprocessor_context_cache: Default::default(),
22765            active_source_context: None,
22766            c_tag_semantics: false,
22767            c_tag_scope_witness: false,
22768            recovered_class_sibling_scopes: HashMap::default(),
22769            consumed_fragment_regions: Vec::new(),
22770            orphaned_namespaces: &index,
22771            partitioned_regions: Vec::new(),
22772            namespace_forward_scans: HashMap::default(),
22773            recovery_captures: Vec::new(),
22774            object_macro_fields: HashMap::default(),
22775            ambiguous_object_macro_fields: HashSet::default(),
22776        };
22777        let recovered = visitor
22778            .recovered_namespace_scope(tail_node, &scope)
22779            .expect("the tail must use the stolen namespace scope");
22780        assert_eq!(recovered.package_name, "testing::internal");
22781        assert!(
22782            recovered.class_unit.is_none(),
22783            "recovered namespace declarations cannot retain the malformed class owner"
22784        );
22785        assert!(recovered.template_signature.is_none());
22786        assert!(recovered.template_metadata.is_none());
22787        assert!(!recovered.declarations_are_fields);
22788        assert!(!recovered.recovered_specialization_member_scope);
22789        assert!(
22790            declarations
22791                .iter()
22792                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
22793            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
22794        );
22795        assert!(
22796            declarations
22797                .iter()
22798                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
22799            "the recovered free function remains in its namespace: {declarations:#?}"
22800        );
22801        assert!(
22802            declarations
22803                .iter()
22804                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
22805            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
22806        );
22807        assert!(
22808            declarations.iter().all(|unit| {
22809                !unit
22810                    .short_name()
22811                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
22812            }),
22813            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
22814        );
22815        assert!(
22816            declarations
22817                .iter()
22818                .all(|unit| !unit.identifier().is_empty()),
22819            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
22820        );
22821    }
22822
22823    /// #4292: a recovered namespace level whose close the brace stack could not
22824    /// prove keeps its head-only range, and the first declaration the walk
22825    /// reaches inside the region is not the namespace. The Module's name
22826    /// occurrence must be the level's own name token; deriving it from the
22827    /// walk's current node paired the head range with an unrelated name and
22828    /// panicked the collector's containment assertion.
22829    #[test]
22830    fn recovered_namespace_levels_name_their_own_name_tokens() {
22831        // #3084's collapsed-head fixture without the namespace's own close:
22832        // the file ends inside the namespace, so the brace stack proves no
22833        // close for its `{` and the level keeps its `namespace Name {` head
22834        // alone (#3328).
22835        let source = r#"namespace outer::app {
22836
22837    class Target {
22838        int value_;
22839    };
22840
22841    template <typename T>
22842    class Holder {
22843    public:
22844        explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
22845
22846#define HOLDER_DEFINE_OP( id, op )                                             \
22847    template <typename U>                                                      \
22848    constexpr friend auto operator op( Holder&& lhs, U&& rhs )                 \
22849        -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> {       \
22850        return Target{};                                                       \
22851    }
22852
22853        HOLDER_DEFINE_OP( equal, == )
22854#undef HOLDER_DEFINE_OP
22855        T m_lhs;
22856    };
22857
22858    class Tail {};
22859"#;
22860        let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
22861        let mut parser = tree_sitter::Parser::new();
22862        parser
22863            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22864            .unwrap();
22865        let tree = parser.parse(source, None).unwrap();
22866        assert!(
22867            tree.root_node().has_error(),
22868            "the fixture must reproduce the parse damage"
22869        );
22870        let target_start = source.find("class Target").expect("target class");
22871        let target_node = tree
22872            .root_node()
22873            .named_descendant_for_byte_range(target_start, target_start + "class".len())
22874            .expect("target class AST node");
22875        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22876        // The macro-declaration grammar keeps the namespace head and its
22877        // members inside a surviving `namespace_definition`; the missing close
22878        // no longer collapses the head into an ERROR, so the scope authority
22879        // answers from parsed ancestry rather than a recovered region.
22880        assert_eq!(
22881            index.enclosing_namespace_components(target_node, source),
22882            ["outer", "app"]
22883        );
22884        let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
22885        let mut recovered_parsed = ParsedFile::new(String::new());
22886        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22887        let properties = RefCell::new(HashMap::default());
22888        let dependencies =
22889            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22890        let ancestry = ParentIndex::new(tree.root_node());
22891        let mut visitor = CppVisitor {
22892            file: &file,
22893            source,
22894            parsed: &mut recovered_parsed,
22895            source_collector: &collector,
22896            source_properties: &properties,
22897            source_dependencies: &dependencies,
22898            primary_ancestry: &ancestry,
22899            primary_root_id: tree.root_node().id(),
22900            source_links: HashMap::default(),
22901            source_declarations: HashSet::default(),
22902            preprocessor_context_cache: Default::default(),
22903            active_source_context: None,
22904            c_tag_semantics: false,
22905            c_tag_scope_witness: false,
22906            recovered_class_sibling_scopes: HashMap::default(),
22907            consumed_fragment_regions: Vec::new(),
22908            orphaned_namespaces: &index,
22909            partitioned_regions: Vec::new(),
22910            namespace_forward_scans: HashMap::default(),
22911            recovery_captures: Vec::new(),
22912            object_macro_fields: HashMap::default(),
22913            ambiguous_object_macro_fields: HashSet::default(),
22914        };
22915        // The surviving `namespace_definition` takes the ordinary namespace
22916        // visit, which declares each level with its own name token.
22917        visitor.visit_container(
22918            tree.root_node(),
22919            &ancestry,
22920            "",
22921            None,
22922            None,
22923            None,
22924            Vec::new(),
22925            Arc::default(),
22926        );
22927        let collector = collector.borrow();
22928        for (module_name, expected_name) in [
22929            (
22930                "outer",
22931                Range {
22932                    start_byte: 10,
22933                    end_byte: 15,
22934                    start_line: 1,
22935                    end_line: 1,
22936                },
22937            ),
22938            (
22939                "outer::app",
22940                Range {
22941                    start_byte: 17,
22942                    end_byte: 20,
22943                    start_line: 1,
22944                    end_line: 1,
22945                },
22946            ),
22947        ] {
22948            let declaration = visitor
22949                .source_links
22950                .iter()
22951                .find(|(unit, _)| unit.fq_name() == module_name)
22952                .expect("the recovered module links a source declaration")
22953                .1
22954                .last()
22955                .expect("the recovered module declaration");
22956            let declaration = collector.declaration(*declaration);
22957            let name = declaration
22958                .name
22959                .expect("the recovered module records its name occurrence");
22960            assert_eq!(
22961                collector.occurrence(name).range,
22962                expected_name,
22963                "the recovered module names its own token: {module_name}"
22964            );
22965        }
22966        assert!(
22967            parsed.declarations().iter().any(|unit| {
22968                unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
22969            }),
22970            "the recovered namespace members keep their namespace: {:#?}",
22971            parsed.declarations()
22972        );
22973    }
22974
22975    #[test]
22976    #[hardwon(
22977        "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22978    )]
22979    fn object_like_field_macros_materialize_owner_specific_declarations() {
22980        let source = r#"#define PUBLIC_FIELDS int public_value;
22981#define PRIVATE_FIELDS int private_value;
22982#define NOT_A_FIELD_LIST not a declaration
22983
22984struct First {
22985  PUBLIC_FIELDS
22986  PRIVATE_FIELDS
22987};
22988struct Second {
22989  PUBLIC_FIELDS
22990  NOT_A_FIELD_LIST
22991};
22992#undef PUBLIC_FIELDS
22993struct Third {
22994  PUBLIC_FIELDS
22995};
22996"#;
22997        let parsed = parse_cpp_declarations(source, "macro-fields.c");
22998        let fields = parsed
22999            .declarations()
23000            .iter()
23001            .filter(|unit| unit.is_field())
23002            .map(|unit| unit.fq_name())
23003            .collect::<Vec<_>>();
23004
23005        assert!(
23006            fields.contains(&"First.public_value".to_string()),
23007            "{fields:?}"
23008        );
23009        assert!(
23010            fields.contains(&"First.private_value".to_string()),
23011            "{fields:?}"
23012        );
23013        assert!(
23014            fields.contains(&"Second.public_value".to_string()),
23015            "{fields:?}"
23016        );
23017        assert!(
23018            !fields.iter().any(|field| field.contains("not_a_field")),
23019            "malformed macro must fail closed: {fields:?}"
23020        );
23021        assert!(
23022            !fields.iter().any(|field| field.starts_with("Third.")),
23023            "undefined macro must fail closed: {fields:?}"
23024        );
23025    }
23026
23027    #[test]
23028    #[hardwon(
23029        "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23030    )]
23031    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
23032        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
23033        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
23034        let mut macros = parsed
23035            .declarations()
23036            .iter()
23037            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
23038            .collect::<Vec<_>>();
23039        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
23040
23041        assert_eq!(macros.len(), 2, "{macros:#?}");
23042        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
23043        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
23044        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
23045        assert_eq!(
23046            parsed.declaration_ranges(macros[1])[0].start_byte,
23047            source.rfind("#define VALUE 2").expect("second definition")
23048        );
23049    }
23050
23051    #[test]
23052    #[hardwon(
23053        "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
23054    )]
23055    fn identifies_export_macro_class_base_displaced_into_declarator() {
23056        let source = r#"#define PROJECT_API_
23057namespace project {
23058namespace internal {
23059template <typename T>
23060class Base {};
23061}
23062template <typename T>
23063class Wrapper;
23064template <>
23065class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
23066}
23067"#;
23068        let mut parser = tree_sitter::Parser::new();
23069        parser
23070            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23071            .unwrap();
23072        let tree = parser.parse(source, None).unwrap();
23073        let start = source.find("internal::Base<int>").expect("base");
23074        let mut base = tree
23075            .root_node()
23076            .descendant_for_byte_range(start, start + 8)
23077            .expect("base syntax");
23078        while base.kind() != "qualified_identifier" {
23079            base = base.parent().expect("qualified base ancestor");
23080        }
23081        assert!(
23082            is_recovered_exported_class_base_type_node(base, source),
23083            "{}",
23084            tree.root_node().to_sexp()
23085        );
23086    }
23087
23088    fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
23089        parsed
23090            .declarations()
23091            .iter()
23092            .filter(|unit| unit.is_function())
23093            .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
23094            .collect()
23095    }
23096
23097    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
23098        let mut identities = all_function_identities(parsed)
23099            .into_iter()
23100            .map(|(name, signature)| (name, signature.unwrap_or_default()))
23101            .collect::<Vec<_>>();
23102        identities.sort();
23103        identities
23104    }
23105
23106    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
23107    /// prototype macros. Each case is parsed independently so a preceding
23108    /// malformed line cannot flatten the next declaration's fields into one
23109    /// cascading `ERROR`; that shape has no structural proof and must fail
23110    /// closed rather than fall back to a token scan. Parameter names are
23111    /// dropped from signatures just as they are for ordinary C prototypes.
23112    #[test]
23113    #[hardwon(
23114        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
23115    )]
23116    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
23117        let cases = [
23118            (
23119                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
23120                "pg_typemap_fit_to_result",
23121                "(VALUE, VALUE)",
23122            ),
23123            (
23124                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
23125                "pg_typemap_result_value",
23126                "(t_typemap *, VALUE, int, int)",
23127            ),
23128            (
23129                "void pg_typemap_mark _(( void * ));",
23130                "pg_typemap_mark",
23131                "(void *)",
23132            ),
23133            (
23134                "void init_pg_type_map _(( void ));",
23135                "init_pg_type_map",
23136                "(void)",
23137            ),
23138            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
23139            (
23140                "extern VALUE pg_extern _(( VALUE, VALUE ));",
23141                "pg_extern",
23142                "(VALUE, VALUE)",
23143            ),
23144            (
23145                "size_t pg_typemap_memsize _(( const void * ));",
23146                "pg_typemap_memsize",
23147                "(const void *)",
23148            ),
23149            (
23150                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
23151                "pg_wrap_socket_io",
23152                "(int, VALUE, VALUE *, int *)",
23153            ),
23154            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
23155            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
23156            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
23157        ];
23158
23159        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
23160            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
23161            assert_eq!(
23162                function_identities(&parsed),
23163                vec![(name.to_string(), signature.to_string())],
23164                "{source}: {:#?}",
23165                parsed.declarations()
23166            );
23167            assert!(
23168                parsed.declarations().iter().all(|unit| !unit.is_field()),
23169                "{source} must not retain the malformed field: {:#?}",
23170                parsed.declarations()
23171            );
23172        }
23173    }
23174
23175    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
23176    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
23177    /// it as a multiplication of a type by a call
23178    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
23179    /// wrapped in an `expression_statement` that `has_error()` only because
23180    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
23181    /// here with a clean preceding field so the prototype remains the
23182    /// parser-owned `expression_statement` this recovery requires.
23183    #[test]
23184    #[hardwon(
23185        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23186    )]
23187    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23188        let cases = [
23189            (
23190                "PGconn *pg_get_pgconn _(( VALUE ));",
23191                "pg_get_pgconn",
23192                "(VALUE)",
23193            ),
23194            (
23195                "PGresult* pgresult_get _(( VALUE ));",
23196                "pgresult_get",
23197                "(VALUE)",
23198            ),
23199        ];
23200        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23201            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23202            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23203            assert_eq!(
23204                function_identities(&parsed),
23205                vec![(name.to_string(), signature.to_string())],
23206                "{source}: {:#?}",
23207                parsed.declarations()
23208            );
23209            assert_eq!(
23210                parsed
23211                    .declarations()
23212                    .iter()
23213                    .filter(|unit| unit.is_field())
23214                    .map(|unit| unit.fq_name())
23215                    .collect::<Vec<_>>(),
23216                vec!["rb_mPG".to_string()],
23217                "{source}: {:#?}",
23218                parsed.declarations()
23219            );
23220        }
23221    }
23222
23223    /// #6902. A class-body prototype macro line is a `field_declaration`. It
23224    /// takes the same candidate gate and reparse as a file-scope one, inside
23225    /// the class envelope, so the member keeps its owner, its written range,
23226    /// its `static`, and the access its label states.
23227    #[test]
23228    #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23229    fn c_prototype_macro_recovers_class_body_members() {
23230        const SOURCE: &str = concat!(
23231            "class Pg {\n",
23232            "  VALUE wrap _(( VALUE self ));\n",
23233            "public:\n",
23234            "  int invoke __P((int value));\n",
23235            "  static char *name PROTO(( int index ));\n",
23236            "};\n",
23237        );
23238        let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23239        assert_eq!(
23240            function_identities(&parsed),
23241            vec![
23242                ("Pg.invoke".to_string(), "(int)".to_string()),
23243                ("Pg.name".to_string(), "(int)".to_string()),
23244                ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23245            ],
23246            "{:#?}",
23247            parsed.declarations()
23248        );
23249        assert!(
23250            parsed.declarations().iter().all(|unit| !unit.is_field()),
23251            "the malformed fields must not survive: {:#?}",
23252            parsed.declarations()
23253        );
23254        let class = parsed
23255            .declarations()
23256            .iter()
23257            .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23258            .expect("class Pg");
23259        let mut children = parsed
23260            .children
23261            .get(class)
23262            .into_iter()
23263            .flatten()
23264            .map(|unit| unit.fq_name())
23265            .collect::<Vec<_>>();
23266        children.sort();
23267        assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23268        for (name, written) in [
23269            ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23270            ("Pg.invoke", "int invoke __P((int value));"),
23271            ("Pg.name", "static char *name PROTO(( int index ));"),
23272        ] {
23273            let unit = parsed
23274                .declarations()
23275                .iter()
23276                .find(|unit| unit.is_function() && unit.fq_name() == name)
23277                .unwrap_or_else(|| panic!("missing recovered member {name}"));
23278            let [range] = parsed.declaration_ranges(unit) else {
23279                panic!("{name} must have exactly one range");
23280            };
23281            assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23282        }
23283        assert_eq!(
23284            recorded_callable_modifiers(&parsed),
23285            vec![
23286                "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23287                    .to_string(),
23288                "Pg.name decl_only=true static=true constructor=false visibility=Public"
23289                    .to_string(),
23290                "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23291                    .to_string(),
23292            ],
23293        );
23294    }
23295
23296    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
23297    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
23298    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
23299    /// regions. The witnessed declaration must still become a Function and
23300    /// its type must not survive as the name of a bogus Field. This test does
23301    /// not claim that structurally flattened neighboring prototypes are safe
23302    /// to recover.
23303    #[test]
23304    #[hardwon(
23305        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23306    )]
23307    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23308        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
23309VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
23310int pg_typemap_fit_to_copy_get                         _(( VALUE ));
23311VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
23312t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
23313VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
23314void pg_typemap_mark                                   _(( void * ));
23315size_t pg_typemap_memsize                              _(( const void * ));
23316void pg_typemap_compact                                _(( void * ));
23317
23318PGconn *pg_get_pgconn                                  _(( VALUE ));
23319t_pg_connection *pg_get_connection                     _(( VALUE ));
23320VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
23321#ifdef __GNUC__
23322__attribute__((format(printf, 3, 4)))
23323#endif
23324NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
23325VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23326void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23327
23328
23329VALUE pg_new_result                                    _(( PGresult *, VALUE ));
23330VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
23331PGresult* pgresult_get                                 _(( VALUE ));
23332VALUE pg_result_check                                  _(( VALUE ));
23333VALUE pg_result_clear                                  _(( VALUE ));
23334VALUE pg_tuple_new                                     _(( VALUE, int ));
23335
23336/*
23337 * Fetch the data pointer for the result object
23338 */
23339static inline t_pg_result *
23340pgresult_get_this( VALUE self )
23341{
23342	return RTYPEDDATA_DATA(self);
23343}
23344
23345
23346rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
23347const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
23348rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
23349
23350"#;
23351        let parsed = parse_cpp_declarations(source, "pg.h");
23352        assert!(
23353            function_identities(&parsed)
23354                .iter()
23355                .any(|(name, signature)| name == "pg_typemap_result_value"
23356                    && signature == "(t_typemap *, VALUE, int, int)"),
23357            "the real issue witness must be a Function with its C signature: {:#?}",
23358            parsed.declarations()
23359        );
23360        assert!(
23361            parsed
23362                .declarations()
23363                .iter()
23364                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23365            "the issue witness must not leave its return type as a Field name: {:#?}",
23366            parsed.declarations()
23367        );
23368    }
23369
23370    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
23371    /// between `explicit` and a constructor name onto the name, producing a
23372    /// `qualified_identifier` whose `::` it had to invent. The declared member
23373    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
23374    /// macro in type position, was already recovered and is the control that
23375    /// both spellings agree.
23376    #[test]
23377    #[hardwon(
23378        "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23379    )]
23380    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23381        let source = r#"class SIMD_4x26 final {
23382   public:
23383      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23384      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23385      int m_v;
23386};
23387"#;
23388        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23389        assert_eq!(
23390            function_identities(&parsed),
23391            vec![
23392                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
23393                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
23394            ],
23395            "{:#?}",
23396            parsed.declarations()
23397        );
23398    }
23399
23400    #[test]
23401    #[hardwon(
23402        "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
23403    )]
23404    fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
23405        let source = r#"struct cpu_spec {};
23406static struct cpu_spec __initdata cpu_specs[];
23407static struct cpu_spec ns::cpu_specs[];
23408"#;
23409        let tree = crate::diagnostics::parse_cpp_tree(source);
23410        let root = tree.root_node();
23411        let declarations = root
23412            .named_children(&mut root.walk())
23413            .filter(|node| node.kind() == "declaration")
23414            .collect::<Vec<_>>();
23415        let names = declarations
23416            .iter()
23417            .filter_map(|declaration| declaration.child_by_field_name("declarator"))
23418            .filter_map(|declarator| extract_variable_name(declarator, source))
23419            .collect::<Vec<_>>();
23420        assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
23421    }
23422
23423    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
23424    /// `ERROR` holding the wrapped declaration and every declaration after it
23425    /// until the parser recovers. All of them must be indexed, each with the
23426    /// byte range that spells it.
23427    #[test]
23428    #[hardwon(
23429        "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
23430    )]
23431    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
23432        let source = r#"#include <cstdint>
23433struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
23434    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
23435                     struct llama_model * model,
23436              struct llama_context_params   params),
23437            "use llama_init_from_model instead");
23438    LLAMA_API int32_t llama_tokenize(
23439        const struct llama_vocab * vocab,
23440                      const char * text,
23441                            bool   parse_special);
23442    LLAMA_API int32_t llama_other(int a);
23443"#;
23444        let parsed = parse_cpp_declarations(source, "llama.h");
23445        assert_eq!(
23446            function_identities(&parsed),
23447            vec![
23448                (
23449                    "llama_new_context_with_model".to_string(),
23450                    "(struct llama_model *, struct llama_context_params)".to_string()
23451                ),
23452                ("llama_other".to_string(), "(int)".to_string()),
23453                (
23454                    "llama_tokenize".to_string(),
23455                    "(const struct llama_vocab *, const char *, bool)".to_string()
23456                ),
23457            ],
23458            "{:#?}",
23459            parsed.declarations()
23460        );
23461
23462        // Each recovered declaration owns the source that spells it, so
23463        // navigation lands on the declaration and not on the macro envelope.
23464        for (name, expected) in [
23465            (
23466                "llama_new_context_with_model",
23467                "LLAMA_API struct llama_context * llama_new_context_with_model(",
23468            ),
23469            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
23470            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
23471        ] {
23472            let unit = parsed
23473                .declarations()
23474                .iter()
23475                .find(|unit| unit.is_function() && unit.fq_name() == name)
23476                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
23477            let [range] = parsed.declaration_ranges(unit) else {
23478                panic!("{name} must have exactly one range");
23479            };
23480            let text = &source[range.start_byte..range.end_byte];
23481            assert!(
23482                text.starts_with(expected),
23483                "{name} range is {text:?}, expected it to start with {expected:?}"
23484            );
23485            assert!(
23486                text.ends_with(')') || text.ends_with(';'),
23487                "{name}: {text:?}"
23488            );
23489        }
23490    }
23491
23492    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
23493    /// invocation's `(` at the end of its line. The macro-declaration grammar
23494    /// keeps each invocation in its own declaration-scope `ERROR`: the wrapped
23495    /// declaration and the hint's exploded words sit in the parameter list,
23496    /// the invocation's `)` closes that list, and its `;` is left to the
23497    /// sibling `expression_statement`. (Before the macro-declaration grammar,
23498    /// the second invocation's envelope swallowed the third into a run of bare
23499    /// siblings; #3094 read that flattening from the token order, and the
23500    /// collapsed-run recovery still owns the envelope/statement pairing the
23501    /// same way.)
23502    ///
23503    /// The identifiers are `library_*` at whisper's own lengths on purpose:
23504    /// tree-sitter prices error recovery partly by how much input a repair
23505    /// skips, and the same construct written with shorter names parses cleanly.
23506    #[test]
23507    #[hardwon(
23508        "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
23509    )]
23510    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
23511        let source = r#"    LIBRARY_DEPRECATED(
23512        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23513        "use library_init_from_file_with_params instead"
23514    );
23515    LIBRARY_DEPRECATED(
23516        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
23517        "use library_init_from_buffer_with_params instead"
23518    );
23519    LIBRARY_DEPRECATED(
23520        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
23521        "use library_init_with_params instead"
23522    );
23523"#;
23524        let mut parser = tree_sitter::Parser::new();
23525        parser
23526            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23527            .expect("C++ grammar");
23528        let tree = parser.parse(source, None).expect("C++ tree");
23529        let root = tree.root_node();
23530
23531        // The shape this recovery exists for: three sibling `ERROR` envelopes,
23532        // each holding one whole invocation, each followed by the statement
23533        // that owns the invocation's `;`.
23534        let mut cursor = root.walk();
23535        let items = root.children(&mut cursor).collect::<Vec<_>>();
23536        let [
23537            first,
23538            first_statement,
23539            second,
23540            second_statement,
23541            third,
23542            third_statement,
23543        ] = items.as_slice()
23544        else {
23545            panic!("{}", root.to_sexp());
23546        };
23547        let envelopes = [
23548            (first, first_statement),
23549            (second, second_statement),
23550            (third, third_statement),
23551        ];
23552        for (envelope, statement) in envelopes {
23553            assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
23554            assert_eq!(
23555                statement.kind(),
23556                "expression_statement",
23557                "{}",
23558                root.to_sexp()
23559            );
23560            assert!(
23561                node_text(*envelope, source).ends_with(')'),
23562                "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
23563                root.to_sexp()
23564            );
23565        }
23566
23567        // Each is admitted, and each names the byte just past its own `;`. A
23568        // self-contained invocation swallows nothing, so the recovery owns
23569        // only its own bytes.
23570        let ancestry = ParentIndex::new(root);
23571        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
23572            .expect("the first invocation");
23573        assert_eq!(
23574            &source[..first_run.invocation_end],
23575            &source[..source.find("instead\"\n    );").expect("first hint")
23576                + "instead\"\n    );".len()]
23577        );
23578        for (envelope, _) in envelopes {
23579            let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
23580                .expect("each invocation");
23581            assert_eq!(
23582                run.region_end, run.invocation_end,
23583                "a self-contained invocation owns only its own bytes"
23584            );
23585        }
23586
23587        let parsed = parse_cpp_declarations(source, "library.h");
23588        assert_eq!(
23589            function_identities(&parsed),
23590            vec![
23591                (
23592                    "library_init".to_string(),
23593                    "(struct library_model_loader *)".to_string()
23594                ),
23595                (
23596                    "library_init_from_buffer".to_string(),
23597                    "(void *, size_t)".to_string()
23598                ),
23599                (
23600                    "library_init_from_file".to_string(),
23601                    "(const char *)".to_string()
23602                ),
23603            ],
23604            "{:#?}",
23605            parsed.declarations()
23606        );
23607    }
23608
23609    #[test]
23610    #[hardwon(
23611        "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
23612    )]
23613    fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
23614        let source = r#"void host() {
23615#ifdef ENABLED
23616LIBRARY_DEPRECATED(
23617    int hidden(const char* value),
23618    "hint"
23619);
23620#endif
23621}
23622"#;
23623        let mut parser = tree_sitter::Parser::new();
23624        parser
23625            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23626            .expect("C++ grammar");
23627        let tree = parser.parse(source, None).expect("C++ tree");
23628        let root = tree.root_node();
23629        let function = root.named_child(0).expect("ordinary function");
23630        let body = function
23631            .child_by_field_name("body")
23632            .expect("ordinary function body");
23633        let conditional = body.named_child(0).expect("conditional body");
23634        let mut conditional_cursor = conditional.walk();
23635        let envelope = conditional
23636            .named_children(&mut conditional_cursor)
23637            .find(|child| child.kind() == "ERROR")
23638            .expect("macro error");
23639        assert_eq!(conditional.kind(), "preproc_ifdef");
23640        assert_eq!(envelope.kind(), "ERROR");
23641
23642        let ancestry = ParentIndex::new(root);
23643        assert!(declaration_scope_container(envelope, &ancestry).is_none());
23644        assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
23645
23646        let parsed = parse_cpp_declarations(source, "function-body-macro.h");
23647        assert!(
23648            parsed
23649                .declarations()
23650                .iter()
23651                .all(|unit| unit.fq_name() != "hidden"),
23652            "a function-body macro error must not become a global declaration: {:#?}",
23653            parsed.declarations()
23654        );
23655    }
23656
23657    /// The hand-off that keeps the scan finite. An invocation that reaches the
23658    /// end of its own declaration scope swallowed nothing, and the ordinary
23659    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
23660    /// that region for every invocation it finds, so admitting it here would
23661    /// hand the scan its own input back.
23662    #[test]
23663    #[hardwon(
23664        "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
23665    )]
23666    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
23667        let source = r#"LIBRARY_DEPRECATED(
23668        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23669        "use library_init_from_file_with_params instead"
23670    );"#;
23671        let mut parser = tree_sitter::Parser::new();
23672        parser
23673            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23674            .expect("C++ grammar");
23675        let tree = parser.parse(source, None).expect("C++ tree");
23676        let root = tree.root_node();
23677        let head = root.named_child(0).expect("the invocation");
23678        let ancestry = ParentIndex::new(root);
23679        assert!(
23680            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
23681            "{}",
23682            root.to_sexp()
23683        );
23684        assert!(
23685            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
23686            "the ordinary reader must be the one that has it: {}",
23687            root.to_sexp()
23688        );
23689        // The index is a speed substitution, never a semantic one: the
23690        // unindexed path is `Node::parent` itself and must agree.
23691        let unindexed = ParentIndex::unindexed();
23692        assert!(
23693            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
23694            "indexed and unindexed climbs must agree"
23695        );
23696        assert_eq!(
23697            macro_wrapped_declarations(head, source, &ancestry).len(),
23698            macro_wrapped_declarations(head, source, &unindexed).len(),
23699            "indexed and unindexed climbs must agree"
23700        );
23701    }
23702
23703    /// Negative controls for the same recovery: a macro invocation whose
23704    /// arguments are not a declaration recovers nothing, whether the parser
23705    /// keeps it clean, reads the arguments as a type, or reads them as a bare
23706    /// declarator.
23707    #[test]
23708    #[hardwon(
23709        "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
23710    )]
23711    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
23712        for source in [
23713            "int before;\nFOO(1, 2);\nint after;\n",
23714            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
23715            "int before;\nMACRO(int a, int b);\nint after;\n",
23716            "DECLARE_HANDLE(HWND);\nint after;\n",
23717        ] {
23718            let parsed = parse_cpp_declarations(source, "macro-call.h");
23719            assert_eq!(
23720                function_identities(&parsed),
23721                Vec::new(),
23722                "{source:?} must declare no function: {:#?}",
23723                parsed.declarations()
23724            );
23725        }
23726    }
23727
23728    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
23729    /// makes tree-sitter read the macro as the member's type and its argument
23730    /// list as a parenthesized declarator, which then swallows the attributed
23731    /// member and the member written after it. Both are members.
23732    #[test]
23733    #[hardwon(
23734        "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
23735    )]
23736    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
23737        let source = r#"#include <string_view>
23738namespace Botan {
23739class DL_Group final {
23740   public:
23741      DL_Group() = default;
23742      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23743      DL_Group(std::string_view pem, int format);
23744      size_t get_p() const;
23745};
23746}
23747"#;
23748        let parsed = parse_cpp_declarations(source, "dl_group.h");
23749        assert_eq!(
23750            function_identities(&parsed),
23751            vec![
23752                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
23753                (
23754                    "Botan.DL_Group.DL_Group".to_string(),
23755                    "(std::string_view)".to_string()
23756                ),
23757                (
23758                    "Botan.DL_Group.DL_Group".to_string(),
23759                    "(std::string_view, int)".to_string()
23760                ),
23761                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23762            ],
23763            "{:#?}",
23764            parsed.declarations()
23765        );
23766    }
23767
23768    /// #2552 shape 3, export-macro class. The class head macro makes the body a
23769    /// `compound_statement`, so members come from the region reparse, and one
23770    /// string-attribute member used to make that reparse unindexable -- which
23771    /// left the class with no members at all.
23772    #[test]
23773    #[hardwon(
23774        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
23775    )]
23776    fn an_export_macro_class_keeps_its_string_attribute_members() {
23777        let source = r#"#include <string_view>
23778namespace Botan {
23779class BOTAN_PUBLIC_API(2, 0) DL_Group final {
23780   public:
23781      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23782      DL_Group(std::string_view pem, int format);
23783      size_t get_p() const;
23784};
23785}
23786"#;
23787        let parsed = parse_cpp_declarations(source, "dl_group.h");
23788        assert!(
23789            parsed
23790                .declarations()
23791                .iter()
23792                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
23793            "{:#?}",
23794            parsed.declarations()
23795        );
23796        assert_eq!(
23797            function_identities(&parsed),
23798            vec![
23799                (
23800                    "Botan.DL_Group.DL_Group".to_string(),
23801                    "(std::string_view)".to_string()
23802                ),
23803                (
23804                    "Botan.DL_Group.DL_Group".to_string(),
23805                    "(std::string_view, int)".to_string()
23806                ),
23807                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23808            ],
23809            "{:#?}",
23810            parsed.declarations()
23811        );
23812    }
23813
23814    /// #2552 shape 3, the `= default` variant plus the access-label
23815    /// constructor. The attributed defaulted constructor separates cleanly, but
23816    /// it strands the next member in a bare `ERROR`, and a constructor written
23817    /// with a member-initializer list under `private:` dissolves into
23818    /// expression soup that the reparse recovers from its own source range.
23819    #[test]
23820    #[hardwon(
23821        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
23822    )]
23823    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
23824        let source = r#"namespace Botan {
23825class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
23826   public:
23827      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
23828      XMSS_Parameters(int oid, int len);
23829      size_t len() const;
23830
23831   private:
23832      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
23833            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
23834
23835      int m_oid;
23836      int m_wots_oid;
23837      size_t m_element_size;
23838      size_t m_tree_height;
23839};
23840}
23841"#;
23842        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
23843        let constructors = function_identities(&parsed)
23844            .into_iter()
23845            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
23846            .map(|(_, signature)| signature)
23847            .collect::<Vec<_>>();
23848        assert_eq!(
23849            constructors,
23850            vec![
23851                "()".to_string(),
23852                "(int, int)".to_string(),
23853                "(int, int, size_t, size_t)".to_string(),
23854            ],
23855            "{:#?}",
23856            parsed.declarations()
23857        );
23858    }
23859
23860    /// Negative control for the same rule: a real qualified name spells its
23861    /// `::` in the source, so the separator is present rather than MISSING and
23862    /// the out-of-line definition keeps its owner.
23863    #[test]
23864    #[hardwon(
23865        "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
23866    )]
23867    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
23868        let source = r#"namespace shell {
23869struct Outer {
23870   struct Inner {
23871      Inner(int v);
23872      void run(int v);
23873   };
23874};
23875Outer::Inner::Inner(int v) {}
23876void Outer::Inner::run(int v) {}
23877}
23878"#;
23879        let parsed = parse_cpp_declarations(source, "outer.cpp");
23880        let names = function_identities(&parsed)
23881            .into_iter()
23882            .map(|(fq_name, _)| fq_name)
23883            .collect::<Vec<_>>();
23884        assert!(
23885            names
23886                .iter()
23887                .all(|name| name.starts_with("shell.Outer$Inner.")),
23888            "{names:#?}"
23889        );
23890    }
23891
23892    #[test]
23893    #[hardwon(
23894        "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
23895    )]
23896    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
23897        let source = r#"namespace control {
23898template <typename T>
23899class AnySpan;
23900template <typename T>
23901class ABSL_ATTRIBUTE_VIEW AnySpan {
23902 public:
23903  int begin() const;
23904};
23905}
23906
23907namespace absl {
23908ABSL_NAMESPACE_BEGIN
23909template <typename T>
23910class ABSL_ATTRIBUTE_VIEW Span {
23911 public:
23912  int begin() const;
23913  int back() const;
23914};
23915
23916int begin();
23917int back();
23918}
23919"#;
23920        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
23921        let declarations = parsed.declarations();
23922        assert!(
23923            declarations
23924                .iter()
23925                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
23926        );
23927        for method in ["begin", "back"] {
23928            assert!(declarations.iter().any(|unit| {
23929                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
23930            }));
23931            assert!(
23932                declarations.iter().any(|unit| {
23933                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
23934                })
23935            );
23936        }
23937        assert!(
23938            declarations
23939                .iter()
23940                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
23941        );
23942        assert!(
23943            declarations
23944                .iter()
23945                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
23946        );
23947        assert!(
23948            declarations
23949                .iter()
23950                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
23951        );
23952    }
23953
23954    #[test]
23955    #[hardwon(
23956        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23957    )]
23958    fn explicit_global_member_definition_has_canonical_package_boundary() {
23959        let source = r#"
23960namespace arangodb::aql {
23961class ExecutionPlan {
23962 public:
23963  template<class... Args> Node* createNode(Args&&... args);
23964};
23965}
23966
23967template<class... Args>
23968Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
23969"#;
23970        let parsed = parse_cpp_declarations(source, "global-member.cpp");
23971
23972        assert!(parsed.declarations().iter().any(|unit| {
23973            unit.is_function()
23974                && unit.package_name() == "arangodb::aql"
23975                && unit.short_name() == "ExecutionPlan.createNode"
23976                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
23977        }));
23978    }
23979
23980    #[test]
23981    #[hardwon(
23982        "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23983    )]
23984    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
23985        let source = r#"
23986#ifndef TINYXML2_INCLUDED
23987#define TINYXML2_INCLUDED
23988namespace tinyxml2 {
23989class TINYXML2_LIB XMLUtil {
23990 public:
23991  static const char* SkipWhiteSpace(const char* p) {
23992    while (*p) {
23993      if (*p == ' ') {
23994        ++p;
23995      }
23996    }
23997    return p;
23998  }
23999  static bool StringEqual(const char* p, const char* q) {
24000    return p == q;
24001  }
24002  class TINYXML2_LIB Helper {
24003   public:
24004    void Touch();
24005  };
24006  static void ToStr(int value, char* buffer);
24007 private:
24008  static const char* writeBoolTrue;
24009};
24010
24011class TINYXML2_LIB XMLNode {
24012 public:
24013  virtual XMLNode* ShallowClone() const = 0;
24014  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
24015};
24016}
24017#endif
24018"#;
24019        let mut parser = tree_sitter::Parser::new();
24020        parser
24021            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24022            .unwrap();
24023        let tree = parser.parse(source, None).unwrap();
24024        let mut boundary_found = false;
24025        walk_named_tree_preorder(tree.root_node(), true, |node| {
24026            if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
24027                && name == "XMLUtil"
24028            {
24029                boundary_found = fragmented_export_sibling_class_boundary(
24030                    node,
24031                    source,
24032                    &ParentIndex::unindexed(),
24033                )
24034                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
24035                .is_some_and(|(_, name, _, _)| name == "XMLNode");
24036            }
24037            WalkControl::Continue
24038        });
24039        assert!(
24040            boundary_found,
24041            "fixture must exercise the recovered sibling boundary"
24042        );
24043
24044        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
24045        assert!(
24046            parsed
24047                .declarations()
24048                .iter()
24049                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
24050            "{:#?}",
24051            parsed.declarations()
24052        );
24053        assert!(
24054            parsed
24055                .declarations()
24056                .iter()
24057                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
24058            "{:#?}",
24059            parsed.declarations()
24060        );
24061        assert!(parsed.declarations().iter().any(|unit| {
24062            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
24063        }));
24064        assert!(
24065            parsed
24066                .declarations()
24067                .iter()
24068                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
24069        );
24070        assert!(
24071            parsed
24072                .declarations()
24073                .iter()
24074                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
24075        );
24076    }
24077
24078    #[test]
24079    #[hardwon(
24080        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24081    )]
24082    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
24083        // Clang's diagnostic suite intentionally contains this ill-formed
24084        // spelling. The analyzer must retain the parser's explicit-global AST
24085        // boundary instead of constructing `cwg311::::cwg311::X`.
24086        let parsed = parse_cpp_declarations(
24087            r#"
24088namespace cwg311 {
24089namespace X { namespace Y {} }
24090namespace ::cwg311::X {}
24091}
24092"#,
24093            "explicit-global-namespace.cpp",
24094        );
24095
24096        assert!(parsed.declarations().iter().any(|unit| {
24097            unit.kind() == CodeUnitType::Module
24098                && unit.short_name() == "cwg311::X"
24099                && unit.fq_name() == "cwg311::X"
24100        }));
24101        assert!(
24102            parsed
24103                .declarations()
24104                .iter()
24105                .all(|unit| !unit.short_name().contains("::::")),
24106            "recovered namespace names must not retain empty scope components: {:#?}",
24107            parsed.declarations()
24108        );
24109    }
24110
24111    #[test]
24112    #[hardwon(
24113        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24114    )]
24115    fn repeated_scope_separator_does_not_create_empty_function_owner() {
24116        let scope = ScopeInfo {
24117            package_name: "X".to_string(),
24118            module: None,
24119            class_unit: None,
24120            template_signature: None,
24121            template_metadata: None,
24122            declarations_are_fields: false,
24123            recovered_specialization_member_scope: false,
24124            visible_using_namespaces: Vec::new(),
24125            local_block: None,
24126            visible_using_declarations: Arc::default(),
24127        };
24128
24129        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
24130
24131        assert!(owner.is_none());
24132        assert_eq!(name, "doit");
24133        assert_eq!(package, "X");
24134    }
24135
24136    #[test]
24137    #[hardwon(
24138        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24139    )]
24140    fn trailing_decltype_expression_is_not_a_function_declarator() {
24141        let source = r#"
24142namespace boost { namespace detail {
24143#if ! defined(BOOST_NO_SFINAE_EXPR) && \
24144    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
24145    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
24146#define BOOST_THREAD_PROVIDES_INVOKE
24147#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
24148template <class Fp, class A0, class ...Args>
24149inline auto
24150invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
24151       BOOST_THREAD_RV_REF(Args) ...args)
24152    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
24153{
24154    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
24155}
24156#endif
24157#endif
24158}}
24159"#;
24160        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
24161
24162        assert!(
24163            parsed
24164                .declarations()
24165                .iter()
24166                .all(|unit| unit.short_name() != ".*f")
24167        );
24168    }
24169
24170    fn find_class_named<'tree>(
24171        root: Node<'tree>,
24172        source: &str,
24173        expected_name: &str,
24174    ) -> Option<Node<'tree>> {
24175        let mut stack = vec![root];
24176        while let Some(node) = stack.pop() {
24177            if node.kind() == "class_specifier"
24178                && node
24179                    .child_by_field_name("name")
24180                    .is_some_and(|name| node_text(name, source) == expected_name)
24181            {
24182                return Some(node);
24183            }
24184            let mut cursor = node.walk();
24185            stack.extend(node.named_children(&mut cursor));
24186        }
24187        None
24188    }
24189
24190    #[test]
24191    #[hardwon(
24192        "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24193    )]
24194    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24195        let source = r#"EXPORT void definition(struct Value value) {}
24196EXPORT void prototype(struct Value value);
24197"#;
24198        let mut parser = tree_sitter::Parser::new();
24199        parser
24200            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24201            .unwrap();
24202        let tree = parser.parse(source, None).unwrap();
24203        let root = tree.root_node();
24204        let mut cursor = root.walk();
24205        let callables = root
24206            .named_children(&mut cursor)
24207            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24208            .collect::<Vec<_>>();
24209
24210        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24211        for callable in callables {
24212            // The macro-declaration grammar parses these cleanly, with the
24213            // export macro retained as a `macro_modifier`; the preserved
24214            // callable and its `struct` parameter are what the sentinel gate
24215            // rejects on, with or without an ERROR in the envelope.
24216            assert!(
24217                cpp_sentinel_macro_parts(callable, source).is_none(),
24218                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24219            );
24220        }
24221    }
24222
24223    #[test]
24224    #[hardwon(
24225        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24226    )]
24227    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24228        let source = r#"namespace absl {
24229ABSL_NAMESPACE_BEGIN
24230// Generate a floating-point variate conforming to a Beta distribution:
24231template <typename RealType = double>
24232class beta_distribution {
24233 public:
24234  using result_type = RealType;
24235
24236
24237  beta_distribution() : beta_distribution(1) {}
24238
24239  explicit beta_distribution(result_type alpha, result_type beta = 1)
24240      : param_(alpha, beta) {}
24241
24242  explicit beta_distribution(const param_type& p) : param_(p) {}
24243
24244  void reset() {}
24245
24246  // Generating functions
24247  template <typename URBG>
24248  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
24249    return (*this)(g, param_);
24250  }
24251
24252};
24253ABSL_NAMESPACE_END
24254}  // namespace absl
24255"#;
24256        let mut parser = tree_sitter::Parser::new();
24257        parser
24258            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24259            .unwrap();
24260        let tree = parser.parse(source, None).unwrap();
24261        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24262        let body = namespace
24263            .child_by_field_name("body")
24264            .expect("fixture namespace body");
24265        let sentinel = body.named_child(0).expect("sentinel envelope");
24266
24267        // The macro-declaration grammar keeps the sentinel head in an `ERROR`
24268        // envelope that ends at the class name, with the class body as the
24269        // sibling it recovered with; the bogus callable the older grammar
24270        // assembled from the last member is no longer part of the envelope.
24271        assert_eq!(sentinel.kind(), "ERROR");
24272        assert!(
24273            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24274            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24275        );
24276
24277        let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24278        for expected in [
24279            "absl.beta_distribution",
24280            "absl.beta_distribution.beta_distribution",
24281            "absl.beta_distribution.reset",
24282        ] {
24283            assert!(
24284                parsed
24285                    .declarations()
24286                    .iter()
24287                    .any(|unit| unit.fq_name() == expected),
24288                "missing recovered declaration {expected}: {:#?}",
24289                parsed.declarations()
24290            );
24291        }
24292    }
24293
24294    #[test]
24295    #[hardwon(
24296        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24297    )]
24298    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24299        let source = r#"namespace absl {
24300ABSL_NAMESPACE_BEGIN
24301// absl::discrete_distribution
24302//
24303// A discrete distribution produces random integers i, where 0 <= i < n
24304template <typename IntType = int>
24305class discrete_distribution {
24306 public:
24307  using result_type = IntType;
24308  class param_type {
24309   public:
24310    param_type() { init(); }
24311    template <typename InputIterator>
24312    explicit param_type(InputIterator begin, InputIterator end)
24313        : p_(begin, end) {
24314      init();
24315    }
24316  };
24317  discrete_distribution() : param_() {}
24318  explicit discrete_distribution(const param_type& p) : param_(p) {}
24319};
24320ABSL_NAMESPACE_END
24321}  // namespace absl
24322"#;
24323        let mut parser = tree_sitter::Parser::new();
24324        parser
24325            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24326            .unwrap();
24327        let tree = parser.parse(source, None).unwrap();
24328        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24329        let body = namespace
24330            .child_by_field_name("body")
24331            .expect("fixture namespace body");
24332        let sentinel = body.named_child(0).expect("sentinel envelope");
24333        let callable = sentinel
24334            .child_by_field_name("declarator")
24335            .and_then(extract_function_declarator)
24336            .and_then(cpp_function_declarator_name_node)
24337            .expect("preserved callable name");
24338
24339        assert_eq!(sentinel.kind(), "function_definition");
24340        assert_eq!(callable.kind(), "identifier");
24341        assert!(
24342            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24343            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24344        );
24345    }
24346
24347    #[test]
24348    #[hardwon(
24349        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24350    )]
24351    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24352        let source = r#"
24353#define CPPCHECKLIB
24354class Library {
24355    struct Container {
24356        CPPCHECKLIB static std::string toString(Yield yield);
24357        CPPCHECKLIB static std::string toString(Action action);
24358    };
24359};
24360"#;
24361        let mut parser = tree_sitter::Parser::new();
24362        parser
24363            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24364            .unwrap();
24365        let tree = parser.parse(source, None).unwrap();
24366        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24367        let parsed = parse_cpp_file(&file, source, &tree);
24368        assert!(
24369            parsed
24370                .declarations()
24371                .iter()
24372                .all(|unit| unit.fq_name() != "Library$Container.std"),
24373            "the qualified return-type namespace must not become a field: {:#?}",
24374            parsed.declarations()
24375        );
24376        for expected in ["(Yield)", "(Action)"] {
24377            assert!(
24378                parsed.declarations().iter().any(|unit| {
24379                    unit.is_function()
24380                        && unit.fq_name() == "Library$Container.toString"
24381                        && unit.signature() == Some(expected)
24382                }),
24383                "recovered toString overload {expected} is missing: {:#?}",
24384                parsed.declarations()
24385            );
24386        }
24387    }
24388
24389    #[test]
24390    #[hardwon(
24391        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24392    )]
24393    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
24394        let source = r#"
24395#define SIMPLECPP_LIB
24396namespace simplecpp {
24397using TokenString = std::string;
24398struct Location { int line{}; };
24399class SIMPLECPP_LIB Token {
24400  TokenString prefix;
24401  void prefix_method() {}
24402 public:
24403  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24404      whitespaceahead(wsahead), location(loc), string(s)
24405      // The comment must not hide the constructor body from recovery.
24406      {
24407      flags();
24408  }
24409  TokenString string;
24410  bool whitespaceahead;
24411  Location location;
24412  Token *previous{};
24413 private:
24414  void flags() {
24415      whitespaceahead = true;
24416  }
24417};
24418}
24419"#;
24420        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
24421
24422        let location_fields = parsed
24423            .declarations()
24424            .iter()
24425            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
24426            .collect::<Vec<_>>();
24427        assert_eq!(
24428            location_fields.len(),
24429            1,
24430            "location should have one class-owned declaration: {:#?}",
24431            parsed.declarations()
24432        );
24433        assert!(
24434            location_fields[0].is_field(),
24435            "location has wrong kind: {:#?}",
24436            parsed.declarations()
24437        );
24438        assert!(
24439            parsed.declarations().iter().all(|unit| {
24440                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
24441            })
24442        );
24443        assert!(
24444            parsed.declarations().iter().all(|unit| {
24445                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24446            })
24447        );
24448        assert!(
24449            parsed
24450                .declarations()
24451                .iter()
24452                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
24453        );
24454        assert!(
24455            parsed
24456                .declarations()
24457                .iter()
24458                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
24459            "the recovered class must retain its constructor: {:#?}",
24460            parsed.declarations()
24461        );
24462        assert!(
24463            parsed
24464                .declarations()
24465                .iter()
24466                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
24467        );
24468        assert!(parsed.declarations().iter().any(|unit| {
24469            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
24470        }));
24471        let constructor = parsed
24472            .declarations()
24473            .iter()
24474            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
24475            .expect("recovered constructor");
24476        let constructor_start = source.find("Token(const").expect("constructor start");
24477        let constructor_end = source
24478            .get(
24479                ..source
24480                    .find("  TokenString string;")
24481                    .expect("constructor end"),
24482            )
24483            .expect("constructor slice")
24484            .trim_end()
24485            .len();
24486        assert!(
24487            parsed
24488                .navigation_ranges
24489                .get(constructor)
24490                .is_some_and(|ranges| {
24491                    ranges.iter().any(|range| {
24492                        range.start_byte == constructor_start && range.end_byte == constructor_end
24493                    })
24494                }),
24495            "constructor navigation must span the full body: {:#?}",
24496            parsed.navigation_ranges
24497        );
24498        assert_eq!(
24499            parsed
24500                .signature_metadata
24501                .get(constructor)
24502                .and_then(|metadata| metadata.first())
24503                .and_then(SignatureMetadata::callable_linkage),
24504            Some(CallableLinkage::External)
24505        );
24506        let token_class = parsed
24507            .declarations()
24508            .iter()
24509            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
24510            .expect("recovered Token class");
24511        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
24512        assert!(
24513            parsed
24514                .navigation_ranges
24515                .get(token_class)
24516                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
24517            "class navigation must include the terminating semicolon: {:#?}",
24518            parsed.navigation_ranges
24519        );
24520    }
24521
24522    #[test]
24523    #[hardwon(
24524        "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24525    )]
24526    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
24527        let source = r#"
24528#define SIMPLECPP_LIB
24529namespace simplecpp {
24530using TokenString = std::string;
24531class Macro;
24532struct Location {
24533  unsigned int fileIndex{};
24534  unsigned int line{};
24535  unsigned int col{};
24536};
24537struct Output {
24538  int type;
24539};
24540class SIMPLECPP_LIB Token {
24541 public:
24542  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24543      whitespaceahead(wsahead), location(loc), string(s) {
24544      flags();
24545  }
24546  Token(const Token &tok) :
24547      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
24548      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
24549      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
24550  Token &operator=(const Token &tok) = delete;
24551  const TokenString& str() const { return string; }
24552  void setstr(const std::string &s) { string = s; flags(); }
24553  bool isOneOf(const char ops[]) const;
24554  TokenString macro;
24555  char op;
24556  bool comment;
24557  bool name;
24558  bool number;
24559  bool whitespaceahead;
24560  Location location;
24561  Token *previous{};
24562  Token *next{};
24563 private:
24564  void flags() {
24565      name = !string.empty();
24566      comment = false;
24567      number = false;
24568      op = 0;
24569  }
24570  TokenString string;
24571};
24572}
24573struct Following {
24574  int type;
24575};
24576class SIMPLECPP_LIB Later {
24577 public:
24578  Later(int value) : value(value) {}
24579  int value;
24580};
24581"#;
24582        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
24583        assert!(
24584            parsed
24585                .declarations()
24586                .iter()
24587                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
24588        );
24589        assert!(
24590            !parsed
24591                .declarations()
24592                .iter()
24593                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
24594        );
24595        assert!(
24596            parsed
24597                .declarations()
24598                .iter()
24599                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
24600        );
24601        assert!(
24602            !parsed
24603                .declarations()
24604                .iter()
24605                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24606        );
24607        assert!(
24608            parsed
24609                .declarations()
24610                .iter()
24611                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
24612        );
24613        assert!(
24614            parsed
24615                .declarations()
24616                .iter()
24617                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
24618        );
24619        assert!(
24620            parsed
24621                .declarations()
24622                .iter()
24623                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
24624        );
24625        assert!(
24626            parsed
24627                .declarations()
24628                .iter()
24629                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
24630        );
24631        assert!(
24632            parsed
24633                .declarations()
24634                .iter()
24635                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
24636        );
24637        assert!(
24638            parsed
24639                .declarations()
24640                .iter()
24641                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
24642        );
24643        assert!(parsed.declarations().iter().all(|unit| {
24644            !matches!(
24645                unit.fq_name().as_str(),
24646                "simplecpp.Token.Following" | "simplecpp.Token.Later"
24647            )
24648        }));
24649        assert!(
24650            !parsed
24651                .declarations()
24652                .iter()
24653                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
24654            "the following struct must remain outside the recovered Token class"
24655        );
24656    }
24657
24658    #[test]
24659    #[hardwon(
24660        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24661    )]
24662    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
24663        let source = r#"
24664#define SIMPLECPP_LIB
24665namespace {
24666namespace simplecpp {
24667using TokenString = std::string;
24668struct Location { int line{}; };
24669class SIMPLECPP_LIB HiddenToken {
24670 public:
24671  HiddenToken(const TokenString &s, const Location &loc) :
24672      location(loc), string(s) {
24673      flags();
24674  }
24675  TokenString string;
24676  Location location;
24677  HiddenToken *previous{};
24678 private:
24679  void flags() {}
24680};
24681}
24682}
24683"#;
24684        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
24685        let constructor = parsed
24686            .declarations()
24687            .iter()
24688            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
24689            .expect("recovered anonymous-namespace constructor");
24690        assert_eq!(
24691            parsed
24692                .signature_metadata
24693                .get(constructor)
24694                .and_then(|metadata| metadata.first())
24695                .and_then(SignatureMetadata::callable_linkage),
24696            Some(CallableLinkage::Internal)
24697        );
24698    }
24699
24700    #[test]
24701    #[hardwon(
24702        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24703    )]
24704    fn macro_qualified_static_field_keeps_real_declarator() {
24705        let source = r#"#define JSON_INLINE_VARIABLE
24706struct Reader {
24707static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
24708static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
24709static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
24710};"#;
24711        let mut parser = tree_sitter::Parser::new();
24712        parser
24713            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24714            .unwrap();
24715        let tree = parser.parse(source, None).unwrap();
24716        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
24717        let parsed = parse_cpp_file(&file, source, &tree);
24718        for expected in [
24719            "Reader.npos",
24720            "Reader.other",
24721            "Reader.pointer",
24722            "Reader.reference",
24723        ] {
24724            assert!(
24725                parsed
24726                    .declarations()
24727                    .iter()
24728                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
24729                "real macro-decorated field {expected} is missing: {:#?}",
24730                parsed.declarations()
24731            );
24732        }
24733        assert!(
24734            parsed
24735                .declarations()
24736                .iter()
24737                .all(|unit| unit.fq_name() != "Reader.std"),
24738            "qualified type prefix became a pseudo-field: {:#?}",
24739            parsed.declarations()
24740        );
24741        let root = tree.root_node();
24742        let mut stack = vec![root];
24743        let mut signatures = Vec::new();
24744        // The macro-declaration grammar keeps `JSON_INLINE_VARIABLE` as a
24745        // `macro_modifier`, so the fields arrive as ordinary clean field
24746        // declarations whose declarators carry the signature text the
24747        // bitfield-clause recovery used to reconstruct.
24748        while let Some(current) = stack.pop() {
24749            if current.kind() == "field_declaration" {
24750                let mut cursor = current.walk();
24751                for declarator in current.children_by_field_name("declarator", &mut cursor) {
24752                    signatures.push(render_cpp_field_signature(current, declarator, source));
24753                }
24754            }
24755            let mut cursor = current.walk();
24756            stack.extend(current.named_children(&mut cursor));
24757        }
24758        signatures.sort();
24759        assert_eq!(
24760            signatures,
24761            [
24762                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
24763                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
24764                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
24765                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
24766            ]
24767        );
24768    }
24769
24770    fn member_function_linkage(source: &str) -> CallableLinkage {
24771        let mut parser = tree_sitter::Parser::new();
24772        parser
24773            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24774            .unwrap();
24775        let tree = parser.parse(source, None).unwrap();
24776        let ancestry = ParentIndex::new(tree.root_node());
24777        let mut stack = vec![tree.root_node()];
24778        while let Some(node) = stack.pop() {
24779            if node.kind() == "function_definition" {
24780                let mut current = node.parent();
24781                while let Some(parent) = current {
24782                    if matches!(
24783                        parent.kind(),
24784                        "class_specifier" | "struct_specifier" | "union_specifier"
24785                    ) {
24786                        return cpp_callable_linkage(node, source, &ancestry);
24787                    }
24788                    current = parent.parent();
24789                }
24790            }
24791            let mut cursor = node.walk();
24792            stack.extend(node.named_children(&mut cursor));
24793        }
24794        panic!("fixture has no member function definition");
24795    }
24796
24797    #[test]
24798    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
24799        assert_eq!(
24800            member_function_linkage("struct Named { int method() { return 1; } };"),
24801            CallableLinkage::External
24802        );
24803        assert_eq!(
24804            member_function_linkage(
24805                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
24806            ),
24807            CallableLinkage::Internal
24808        );
24809        assert_eq!(
24810            member_function_linkage("struct { int method() { return 1; } } instance;"),
24811            CallableLinkage::Internal
24812        );
24813        assert_eq!(
24814            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
24815            CallableLinkage::Internal
24816        );
24817    }
24818
24819    #[test]
24820    #[hardwon(
24821        "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24822    )]
24823    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
24824        let source = r#"
24825#ifndef PROTON_VALUE_HPP
24826#define PROTON_VALUE_HPP
24827namespace proton {
24828namespace internal {
24829class value_base {
24830  protected:
24831    internal::data& data();
24832    internal::data data_;
24833  friend class codec::encoder;
24834  friend class codec::decoder;
24835};
24836}
24837class value : public internal::value_base, private internal::comparable<value> {
24838  private:
24839    template<class T, class U=void> struct assignable :
24840        public std::enable_if<codec::is_encodable<T>::value, U> {};
24841    template<class U> struct assignable<value, U> {};
24842  public:
24843    PN_CPP_EXTERN value();
24844    PN_CPP_EXTERN value(const value&);
24845    PN_CPP_EXTERN value& operator=(const value&);
24846    PN_CPP_EXTERN value(value&&);
24847    PN_CPP_EXTERN value& operator=(value&&);
24848    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
24849    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
24850        codec::encoder e(*this);
24851        e << x;
24852        return *this;
24853    }
24854    PN_CPP_EXTERN type_id type() const;
24855    PN_CPP_EXTERN bool empty() const;
24856    PN_CPP_EXTERN void clear();
24857    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
24858    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
24859  friend PN_CPP_EXTERN void swap(value&, value&);
24860  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
24861  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
24862  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
24863    value(pn_data_t* d);
24864    void reset(pn_data_t* d = 0);
24865};
24866}
24867#endif
24868"#;
24869        let mut parser = tree_sitter::Parser::new();
24870        parser
24871            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24872            .unwrap();
24873        let tree = parser.parse(source, None).unwrap();
24874        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
24875        let parsed = parse_cpp_file(&file, source, &tree);
24876        // The macro-declaration grammar parses the class and its decorated
24877        // members cleanly, so the constructors mint with their true
24878        // class-owned identity (`proton.value.value`) rather than the
24879        // namespace-scope identity the fragment recovery produced before.
24880        let macro_constructors = parsed
24881            .signature_metadata
24882            .iter()
24883            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
24884            .flat_map(|(_, metadata)| metadata)
24885            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
24886            .collect::<Vec<_>>();
24887
24888        assert_eq!(
24889            macro_constructors.len(),
24890            3,
24891            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
24892            parsed.declarations()
24893        );
24894        assert!(
24895            macro_constructors.iter().all(|metadata| {
24896                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
24897            }),
24898            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
24899        );
24900    }
24901
24902    #[test]
24903    #[hardwon(
24904        "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
24905    )]
24906    fn recovered_export_class_typedef_uses_displaced_alias_name() {
24907        let source = r#"
24908namespace spi {
24909class Filter {
24910public:
24911    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
24912};
24913}
24914namespace filter {
24915class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
24916{
24917public:
24918    typedef spi::Filter BASE_CLASS;
24919    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
24920    BEGIN_LOG4CXX_CAST_MAP()
24921    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
24922    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
24923    END_LOG4CXX_CAST_MAP()
24924    FilterDecision decide() const;
24925};
24926}
24927"#;
24928        let mut parser = tree_sitter::Parser::new();
24929        parser
24930            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24931            .unwrap();
24932        let tree = parser.parse(source, None).unwrap();
24933        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
24934        let parsed = parse_cpp_file(&file, source, &tree);
24935        assert!(
24936            parsed.declarations().iter().any(|unit| {
24937                unit.is_class()
24938                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
24939                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
24940            }),
24941            "the displaced typedef alias must retain its declared name: {:#?}",
24942            parsed.declarations()
24943        );
24944        assert!(
24945            parsed
24946                .declarations()
24947                .iter()
24948                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
24949            "the qualified underlying type must not become a false nested alias: {:#?}",
24950            parsed.declarations()
24951        );
24952    }
24953
24954    #[test]
24955    #[hardwon(
24956        "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24957    )]
24958    fn exported_single_base_recovery_uses_displaced_class_name() {
24959        let source = r#"
24960class CORE_EXPORT QgsPoint : public AbstractGeometry
24961{
24962    Q_GADGET
24963
24964    Q_PROPERTY( double x READ x WRITE setX )
24965    Q_PROPERTY( double y READ y WRITE setY )
24966    Q_PROPERTY( double z READ z WRITE setZ )
24967    Q_PROPERTY( double m READ m WRITE setM )
24968
24969  public:
24970#ifndef SIP_RUN
24971    QgsPoint(
24972      double x = std::numeric_limits<double>::quiet_NaN(),
24973      double y = std::numeric_limits<double>::quiet_NaN(),
24974      double z = std::numeric_limits<double>::quiet_NaN(),
24975      double m = std::numeric_limits<double>::quiet_NaN(),
24976      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
24977    );
24978#else
24979    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 )];
24980    % MethodCode
24981    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
24982    {
24983      int state;
24984      sipIsErr = 0;
24985      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
24986      if ( !sipIsErr )
24987      {
24988        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
24989      }
24990      sipReleaseType( p, sipType_QgsPointXY, state );
24991    }
24992    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
24993    {
24994      int state;
24995      sipIsErr = 0;
24996
24997      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
24998      if ( !sipIsErr )
24999      {
25000        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25001      }
25002      sipReleaseType( p, sipType_QPointF, state );
25003    }
25004    else if (
25005      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
25006      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
25007      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
25008      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
25009    {
25010      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
25011      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
25012      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
25013      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
25014      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
25015      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
25016    }
25017    else // Invalid ctor arguments
25018    {
25019      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
25020      sipIsErr = 1;
25021    }
25022    % End
25023#endif
25024
25025    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
25026    explicit QgsPoint( QPointF p ) SIP_SKIP;
25027    explicit QgsPoint(
25028      Qgis::WkbType wkbType,
25029      double x = std::numeric_limits<double>::quiet_NaN(),
25030      double y = std::numeric_limits<double>::quiet_NaN(),
25031      double z = std::numeric_limits<double>::quiet_NaN(),
25032      double m = std::numeric_limits<double>::quiet_NaN()
25033    ) SIP_SKIP;
25034    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25035    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
25036    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25037#ifndef SIP_RUN
25038  private:
25039    bool fuzzyHelper(
25040      double epsilon,
25041      const AbstractGeometry &other,
25042      bool is3DFlag,
25043      bool isMeasureFlag
25044    ) const
25045    {
25046      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
25047    }
25048#endif
25049};
25050class Ordinary : public Base { public: Ordinary(); };
25051class API_EXPORT Plain { public: Plain(); };
25052class API_EXPORT : public Base {};
25053class
25054PN_CPP_CLASS_EXTERN Sender : public Link {
25055    Sender();
25056    struct impl;
25057    struct impl& get_impl() const;
25058};
25059class thread_ctx_t {};
25060class ctx_t ZMQ_FINAL : public thread_ctx_t {
25061    bool start();
25062};
25063"#;
25064        let mut parser = tree_sitter::Parser::new();
25065        parser
25066            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25067            .unwrap();
25068        let tree = parser.parse(source, None).unwrap();
25069        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
25070        let parsed = parse_cpp_file(&file, source, &tree);
25071        let declarations = parsed.declarations();
25072
25073        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
25074            assert!(
25075                declarations
25076                    .iter()
25077                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
25078                "missing recovered class {expected}: {declarations:#?}"
25079            );
25080        }
25081        let qgs_point = declarations
25082            .iter()
25083            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
25084            .expect("recovered QgsPoint class");
25085        assert_eq!(
25086            parsed.raw_supertypes.get(qgs_point),
25087            Some(&vec!["AbstractGeometry".to_string()]),
25088            "single-base export recovery must retain its displaced base"
25089        );
25090        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
25091        assert!(
25092            parsed
25093                .navigation_ranges
25094                .get(qgs_point)
25095                .is_some_and(|ranges| {
25096                    !ranges.is_empty()
25097                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
25098                }),
25099            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
25100            parsed.navigation_ranges.get(qgs_point)
25101        );
25102        let sender = declarations
25103            .iter()
25104            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
25105            .expect("recovered Sender class");
25106        assert_eq!(
25107            parsed.raw_supertypes.get(sender),
25108            Some(&vec!["Link".to_string()]),
25109            "post-declarator export recovery must retain its displaced base"
25110        );
25111        let recovered_member = declarations
25112            .iter()
25113            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
25114            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
25115        assert_eq!(
25116            parsed
25117                .signature_metadata
25118                .get(recovered_member)
25119                .and_then(|metadata| metadata.first())
25120                .and_then(SignatureMetadata::callable_linkage),
25121            Some(CallableLinkage::External),
25122            "a named recovered class's members have external linkage"
25123        );
25124        let ctx = declarations
25125            .iter()
25126            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
25127            .expect("recovered ctx_t class");
25128        assert_eq!(
25129            parsed.raw_supertypes.get(ctx),
25130            Some(&vec!["thread_ctx_t".to_string()]),
25131            "postfix export-macro recovery must retain its displaced base"
25132        );
25133        assert!(
25134            declarations.iter().any(|unit| {
25135                unit.is_function()
25136                    && unit.fq_name() == "QgsPoint.QgsPoint"
25137                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
25138            }),
25139            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
25140        );
25141        assert!(
25142            declarations.iter().all(|unit| {
25143                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
25144            }),
25145            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
25146        );
25147    }
25148
25149    #[test]
25150    #[hardwon(
25151        "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25152    )]
25153    fn function_like_export_macro_classes_keep_names_and_base_edges() {
25154        // Every head shape after `class MACRO(2, 0)`: a plain body, a single
25155        // base, `final` without a base, `final` with one base, and `final` with
25156        // a comma-separated base list. Older grammars left each in its own
25157        // error shape; brokk-tree-sitter-cpp 0.24.7 reads them all as
25158        // `class_specifier`s, which publish an empty base list for a class
25159        // without bases.
25160        let source = r#"
25161namespace api {
25162class PROJECT_PUBLIC_API(2, 0) Prelude {
25163  public:
25164    Prelude();
25165};
25166class PROJECT_PUBLIC_API(2, 0) Base {
25167  public:
25168    Base(int value);
25169};
25170class PROJECT_PUBLIC_API(2, 0) Mixin {
25171  public:
25172    Mixin();
25173};
25174class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
25175  public:
25176    Adopted(int value);
25177};
25178class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25179  public:
25180    Derived(int value);
25181};
25182class PROJECT_PUBLIC_API(2, 0) Solo final {
25183  public:
25184    Solo();
25185};
25186class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25187  public:
25188    Blended(int value);
25189};
25190class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25191  public:
25192    Woven(int value);
25193};
25194} // namespace api
25195"#;
25196        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25197        let explicit = parse_cpp_declarations(
25198            &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
25199            "function-like-export.hpp",
25200        );
25201        assert_eq!(
25202            declaration_expansion_inventory(&parsed),
25203            declaration_expansion_inventory(&explicit)
25204        );
25205        let declarations = parsed.declarations();
25206        let class_named = |name: &str| {
25207            declarations
25208                .iter()
25209                .find(|unit| unit.is_class() && unit.fq_name() == name)
25210                .unwrap_or_else(|| {
25211                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25212                })
25213        };
25214        let base = class_named("api.Base");
25215        class_named("api.Prelude");
25216        class_named("api.Mixin");
25217
25218        assert_eq!(
25219            parsed.raw_supertypes.get(class_named("api.Adopted")),
25220            Some(&vec!["Base".to_string()])
25221        );
25222        assert_eq!(
25223            parsed.raw_supertypes.get(class_named("api.Derived")),
25224            Some(&vec!["Base".to_string()])
25225        );
25226        // The explicit expansion is the oracle for a class without bases: a
25227        // parsed class publishes an empty base list where recovery published
25228        // none.
25229        let solo = parsed.raw_supertypes.get(class_named("api.Solo"));
25230        assert_eq!(
25231            solo,
25232            explicit.raw_supertypes.get(class_named("api.Solo")),
25233            "a final class without a base list must match its explicit expansion"
25234        );
25235        assert!(
25236            solo.is_none_or(Vec::is_empty),
25237            "a final class without a base list must not invent a supertype"
25238        );
25239        assert_eq!(
25240            parsed.raw_supertypes.get(class_named("api.Blended")),
25241            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25242        );
25243        assert_eq!(
25244            parsed.raw_supertypes.get(class_named("api.Woven")),
25245            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25246        );
25247        assert!(
25248            declarations
25249                .iter()
25250                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25251            "the export macro must not become a declaration: {declarations:#?}"
25252        );
25253        assert!(
25254            declarations.iter().all(|unit| !matches!(
25255                unit.identifier(),
25256                "final" | "public" | "protected" | "private"
25257            )),
25258            "the head specifiers must not become declarations: {declarations:#?}"
25259        );
25260        assert!(
25261            parsed
25262                .navigation_ranges
25263                .get(base)
25264                .is_some_and(|ranges| !ranges.is_empty()),
25265            "the recovered base must retain a navigable declaration range"
25266        );
25267    }
25268
25269    #[test]
25270    #[hardwon(
25271        "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25272    )]
25273    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25274        // #2557: the class name is the last identifier before the head ends
25275        // (`final`, the base clause `:`, or the body), whatever its spelling.
25276        // A class named in capitals (`X509_CA`) is a class, and an object-like
25277        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
25278        // the control whose spelling never mattered.
25279        let source = r#"
25280namespace api {
25281class PROJECT_PUBLIC_API(2, 0) Base {
25282  public:
25283    Base();
25284};
25285class PROJECT_PUBLIC_API(2, 0) Mixin {
25286  public:
25287    Mixin();
25288};
25289class PROJECT_PUBLIC_API(2, 0) Name {
25290  public:
25291    Name();
25292};
25293class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25294  public:
25295    X509_CA();
25296};
25297class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25298  public:
25299    HSS_LMS_KEY();
25300};
25301class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25302  public:
25303    GOST_3410();
25304};
25305class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25306  public:
25307    PKCS11_RSA();
25308};
25309class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25310  public:
25311    Plain();
25312};
25313class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25314  public:
25315    Decorated();
25316};
25317class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25318  public:
25319    Layered();
25320};
25321} // namespace api
25322"#;
25323        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25324        let explicit = parse_cpp_declarations(
25325            &source
25326                .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25327                .replace("FIRST_MACRO SECOND_MACRO ", "")
25328                .replace("OTHER_MACRO ", ""),
25329            "positional-export.hpp",
25330        );
25331        assert_eq!(
25332            declaration_expansion_inventory(&parsed),
25333            declaration_expansion_inventory(&explicit)
25334        );
25335        let declarations = parsed.declarations();
25336        let class_named = |name: &str| {
25337            declarations
25338                .iter()
25339                .find(|unit| unit.is_class() && unit.fq_name() == name)
25340                .unwrap_or_else(|| {
25341                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25342                })
25343        };
25344        for (name, bases) in [
25345            ("api.Name", None),
25346            ("api.X509_CA", None),
25347            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25348            ("api.GOST_3410", Some(vec!["Base"])),
25349            ("api.PKCS11_RSA", None),
25350            ("api.Plain", None),
25351            ("api.Decorated", Some(vec!["Base"])),
25352            ("api.Layered", Some(vec!["Base", "Mixin"])),
25353        ] {
25354            let actual = parsed.raw_supertypes.get(class_named(name));
25355            match bases {
25356                Some(bases) => assert_eq!(
25357                    actual,
25358                    Some(&bases.into_iter().map(str::to_string).collect::<Vec<_>>()),
25359                    "{name}"
25360                ),
25361                // `MACRO(2, 0) OTHER_MACRO Plain {` without a base still reaches
25362                // recovery, which publishes no base list.
25363                None if name == "api.Plain" => assert_eq!(actual, None, "{name}"),
25364                // A head the grammar reads publishes the empty base list of
25365                // its explicit expansion.
25366                None => {
25367                    assert_eq!(
25368                        actual,
25369                        explicit.raw_supertypes.get(class_named(name)),
25370                        "{name} must match its explicit expansion"
25371                    );
25372                    assert!(actual.is_none_or(Vec::is_empty), "{name}");
25373                }
25374            }
25375        }
25376        assert!(
25377            declarations.iter().all(|unit| !matches!(
25378                unit.identifier(),
25379                "PROJECT_PUBLIC_API"
25380                    | "OTHER_MACRO"
25381                    | "FIRST_MACRO"
25382                    | "SECOND_MACRO"
25383                    | "final"
25384                    | "public"
25385            )),
25386            "macros and head specifiers must not become declarations: {declarations:#?}"
25387        );
25388    }
25389
25390    #[test]
25391    #[hardwon(
25392        "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
25393    )]
25394    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
25395        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
25396        // head, the body's `{` and its first member in one declaration-scope
25397        // ERROR, scatters the remaining members across the container's
25398        // siblings, and ends the container on the class's own `}`. The class
25399        // was lost and the macro minted a class of its own.
25400        let source = r#"
25401namespace api {
25402
25403/**
25404* Doc comment
25405*/
25406class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
25407   public:
25408      /**
25409      * Construct from a point.
25410      */
25411      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
25412
25413#if defined(PROJECT_HAS_LEGACY_POINT)
25414      /**
25415      * Construct from a legacy point.
25416      */
25417      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
25418#endif
25419
25420      std::string algo_name() const override;
25421
25422      AlgorithmIdentifier algorithm_identifier() const override;
25423};
25424
25425}
25426"#;
25427        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
25428        let declarations = parsed.declarations();
25429        let class = declarations
25430            .iter()
25431            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
25432            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25433        assert_eq!(
25434            parsed.raw_supertypes.get(class),
25435            Some(&vec!["BaseKey".to_string()]),
25436            "the virtual base is the class's base: {declarations:#?}"
25437        );
25438        for member in [
25439            "api.VirtualBased.algo_name",
25440            "api.VirtualBased.algorithm_identifier",
25441        ] {
25442            assert!(
25443                declarations
25444                    .iter()
25445                    .any(|unit| unit.is_function() && unit.fq_name() == member),
25446                "{member} must be owned by the recovered class: {declarations:#?}"
25447            );
25448        }
25449        assert!(
25450            declarations
25451                .iter()
25452                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25453            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
25454        );
25455    }
25456
25457    #[test]
25458    #[hardwon(
25459        "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
25460    )]
25461    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
25462        // #2924: object-like macro lines before the head demote the `class`
25463        // keyword to a bare identifier and make the macro invocation an
25464        // `init_declarator`, so the head arrives as a `declaration` rather than
25465        // as an ERROR carrying a `class_specifier`. The name is still the last
25466        // identifier before `final`, and `virtual` is a base specifier rather
25467        // than a base.
25468        let source = r#"
25469namespace api {
25470
25471DIAGNOSTIC_PUSH
25472DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
25473
25474class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
25475                                               public virtual api::Inner::Key {
25476   public:
25477      std::string algo_name() const override;
25478};
25479
25480DIAGNOSTIC_POP
25481
25482}
25483"#;
25484        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
25485        let declarations = parsed.declarations();
25486        let class = declarations
25487            .iter()
25488            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
25489            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25490        assert_eq!(
25491            parsed.raw_supertypes.get(class),
25492            Some(&vec![
25493                "api::Outer::Key".to_string(),
25494                "api::Inner::Key".to_string()
25495            ]),
25496            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
25497        );
25498        assert!(
25499            declarations
25500                .iter()
25501                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
25502            "the member is owned by the recovered class: {declarations:#?}"
25503        );
25504        assert!(
25505            declarations
25506                .iter()
25507                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25508            "the macro invocation must not mint a declaration: {declarations:#?}"
25509        );
25510    }
25511
25512    fn function_like_export_class_fixture(head: &str, name: &str) -> String {
25513        format!(
25514            r#"
25515namespace api {{
25516class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
25517   public:
25518      /** Return a descriptive string. */
25519      const char* what() const noexcept override {{ return m_msg.c_str(); }}
25520
25521      /** Return the type of error. */
25522      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
25523
25524      /** Return an associated error code. */
25525      virtual int error_code() const noexcept {{ return 0; }}
25526
25527      /** Avoid throwing the base directly. */
25528      explicit Exception(std::string_view msg);
25529
25530      /** Avoid throwing the base directly. */
25531      Exception(const char* prefix, std::string_view msg);
25532
25533      /** Avoid throwing the base directly. */
25534      Exception(std::string_view msg, const std::exception& e);
25535
25536   private:
25537      std::string m_msg;
25538}};
25539
25540class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
25541   public:
25542      explicit {name}(std::string_view msg);
25543
25544      explicit {name}(std::string_view msg, std::string_view where);
25545
25546      {name}(std::string_view msg, const std::exception& e);
25547
25548      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
25549}};
25550}} // namespace api
25551"#
25552        )
25553    }
25554
25555    #[test]
25556    #[hardwon(
25557        "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
25558    )]
25559    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
25560        // The class after a preceding export class follows the same rule
25561        // (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration. Older
25562        // grammars embedded this head in the preceding class's error; since
25563        // brokk-tree-sitter-cpp 0.24.7 the grammar reads both heads, and the
25564        // class must still be named by position.
25565        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
25566            let source = function_like_export_class_fixture(head, name);
25567            let tree = crate::grammar_test_support::tree(&source);
25568            crate::grammar_test_support::assert_function_like_export_class_head(
25569                &tree, &source, name,
25570            );
25571
25572            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
25573            let explicit = parse_cpp_declarations(
25574                &source
25575                    .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25576                    .replace("OTHER_MACRO ", ""),
25577                "embedded-positional-export.hpp",
25578            );
25579            assert_eq!(
25580                declaration_expansion_inventory(&parsed),
25581                declaration_expansion_inventory(&explicit),
25582                "{head}"
25583            );
25584            let declarations = parsed.declarations();
25585            let class = declarations
25586                .iter()
25587                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
25588                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
25589            assert_eq!(
25590                parsed.raw_supertypes.get(class),
25591                Some(&vec!["Exception".to_string()]),
25592                "{head}"
25593            );
25594            assert!(
25595                declarations
25596                    .iter()
25597                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
25598                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
25599            );
25600        }
25601    }
25602
25603    #[test]
25604    #[hardwon(
25605        "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
25606    )]
25607    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
25608        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
25609        // `class MACRO(3, 6) Name final :` in the error, the first base as the
25610        // `type` of the following declaration, and closes that declaration with
25611        // a zero-width `MISSING identifier`. Reading the head by position must
25612        // not take that missing node for the class name: an empty name panics
25613        // at `FqName` construction, which took the Botan corpus replay down
25614        // (#2557). The head's tokens straddle the two nodes, so the positional
25615        // read spans both and recovers the class rather than leaving the
25616        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
25617        let source = r#"
25618namespace api {
25619class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
25620                                                    public virtual Botan::EC_PublicKey {
25621   public:
25622      std::string algo_name() const override { return "ECDSA"; }
25623};
25624} // namespace api
25625"#;
25626        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
25627        let declarations = parsed.declarations();
25628        assert!(
25629            declarations
25630                .iter()
25631                .all(|unit| !unit.identifier().is_empty()),
25632            "no declaration may carry an empty name: {declarations:#?}"
25633        );
25634        assert!(
25635            declarations
25636                .iter()
25637                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
25638            "macros and head specifiers must not become declarations: {declarations:#?}"
25639        );
25640        let class = declarations
25641            .iter()
25642            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
25643            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25644        assert_eq!(
25645            parsed.raw_supertypes.get(class),
25646            Some(&vec![
25647                "Botan::TPM2::PublicKey".to_string(),
25648                "Botan::EC_PublicKey".to_string()
25649            ]),
25650            "both qualified virtual bases are bases: {declarations:#?}"
25651        );
25652        let source_facts = parsed.source_facts.as_ref().unwrap();
25653        let declaration = parsed
25654            .source_declaration_units
25655            .iter()
25656            .find(|(_, unit)| unit == class)
25657            .map(|(declaration, _)| *declaration)
25658            .expect("recovered class source bridge");
25659        let bases = source_facts
25660            .cpp
25661            .as_ref()
25662            .unwrap()
25663            .declarations
25664            .iter()
25665            .find(|fact| fact.declaration == declaration)
25666            .expect("recovered class source fact")
25667            .bases
25668            .clone();
25669        assert_eq!(
25670            bases
25671                .iter()
25672                .map(|base| base.components.clone())
25673                .collect::<Vec<_>>(),
25674            vec![
25675                vec!["Botan", "TPM2", "PublicKey"],
25676                vec!["Botan", "EC_PublicKey"],
25677            ],
25678            "generalized recovery must publish structured base components"
25679        );
25680        assert!(
25681            bases.iter().all(|base| base.is_virtual),
25682            "generalized recovery must preserve virtual base qualifiers"
25683        );
25684        assert!(
25685            declarations
25686                .iter()
25687                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25688            "the head must not mint a macro-named class: {declarations:#?}"
25689        );
25690        let mixed_source = source.replace(
25691            "public virtual Botan::EC_PublicKey",
25692            "public Botan::EC_PublicKey",
25693        );
25694        let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
25695        let qualifiers = mixed
25696            .source_facts
25697            .as_ref()
25698            .unwrap()
25699            .cpp
25700            .as_ref()
25701            .unwrap()
25702            .declarations
25703            .iter()
25704            .flat_map(|declaration| &declaration.bases)
25705            .map(|base| base.is_virtual)
25706            .collect::<Vec<_>>();
25707        assert_eq!(
25708            qualifiers,
25709            [true, false],
25710            "base qualifiers must not cross the comma"
25711        );
25712    }
25713
25714    #[test]
25715    #[hardwon(
25716        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25717    )]
25718    fn function_like_export_class_survives_a_preceding_malformed_body() {
25719        // Older grammars left the first class's body malformed and embedded the
25720        // second class in its error. Since brokk-tree-sitter-cpp 0.24.7 both
25721        // heads parse, so both classes are ordinary source declarations.
25722        let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
25723        let tree = crate::grammar_test_support::tree(&source);
25724        for name in ["Exception", "Invalid_Argument"] {
25725            crate::grammar_test_support::assert_function_like_export_class_head(
25726                &tree, &source, name,
25727            );
25728        }
25729
25730        let parsed = parse_cpp_file(
25731            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25732            &source,
25733            &tree,
25734        );
25735        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
25736        let explicit = parse_cpp_file(
25737            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25738            &expanded,
25739            &crate::grammar_test_support::tree(&expanded),
25740        );
25741        assert_eq!(
25742            declaration_expansion_inventory(&parsed),
25743            declaration_expansion_inventory(&explicit)
25744        );
25745        assert!(
25746            !explicit
25747                .materialization_records
25748                .iter()
25749                .any(|record| matches!(record, MaterializationRecord::RecoveredDeclaration { .. })),
25750            "the explicit expansion needs no recovery: {:#?}",
25751            explicit.materialization_records
25752        );
25753        let declarations = parsed.declarations();
25754        let exception = declarations
25755            .iter()
25756            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
25757            .expect("qualified-base export class");
25758        let invalid = declarations
25759            .iter()
25760            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
25761            .expect("class embedded in the preceding malformed body");
25762
25763        assert_eq!(
25764            parsed.raw_supertypes.get(exception),
25765            Some(&vec!["std::exception".to_string()])
25766        );
25767        assert_eq!(
25768            parsed.raw_supertypes.get(invalid),
25769            Some(&vec!["Exception".to_string()])
25770        );
25771        assert!(
25772            !parsed.materialization_records.iter().any(|record| matches!(
25773                record,
25774                MaterializationRecord::RecoveredDeclaration { unit, .. }
25775                    if unit == invalid
25776            )),
25777            "a parsed class must not claim recovery provenance: {:#?}",
25778            parsed.materialization_records
25779        );
25780    }
25781
25782    #[test]
25783    #[hardwon(
25784        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25785    )]
25786    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
25787        let source = r#"
25788public:
25789   explicit Lookup_Error(std::string_view err) : Exception(err) {}
25790
25791   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
25792"#;
25793        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
25794            .expect("reparse merged constructor body");
25795        let (range, body) =
25796            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
25797                .unwrap_or_else(|| {
25798                    panic!(
25799                        "the merged constructor must retain its structured declarator/body: {}",
25800                        tree.root_node().to_sexp()
25801                    )
25802                });
25803        assert_eq!(
25804            source.get(range).expect("constructor range"),
25805            "Lookup_Error(std::string_view err) : Exception(err) {}"
25806        );
25807        assert_eq!(node_text(body, source), "{}");
25808    }
25809
25810    #[test]
25811    #[hardwon(
25812        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25813    )]
25814    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
25815        let positive_source =
25816            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
25817        let mut parser = tree_sitter::Parser::new();
25818        parser
25819            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25820            .unwrap();
25821        let positive_tree = parser.parse(positive_source, None).unwrap();
25822        assert!(cpp_reparsed_members_are_indexable(
25823            positive_tree.root_node(),
25824            positive_source
25825        ));
25826
25827        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
25828        let negative_tree = parser.parse(negative_source, None).unwrap();
25829        assert!(!cpp_reparsed_members_are_indexable(
25830            negative_tree.root_node(),
25831            negative_source
25832        ));
25833    }
25834
25835    #[test]
25836    #[hardwon(
25837        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25838    )]
25839    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
25840        let copy_control_source = r#"
25841public:
25842    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
25843    explicit Token(const Token* tok);
25844    ~Token();
25845    Token* astOperand1() { return nullptr; }
25846"#;
25847        let constraint_source = r#"
25848private:
25849    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25850    static T *tokAtImpl(T *tok, int index) {
25851        return tok;
25852    }
25853
25854    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25855    static T *linkAtImpl(T *tok, int index) {
25856        return tok;
25857    }
25858
25859public:
25860    int late() const { return 1; }
25861"#;
25862        let mut parser = tree_sitter::Parser::new();
25863        parser
25864            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25865            .unwrap();
25866        let copy_control_tree = parser
25867            .parse(copy_control_source, None)
25868            .expect("parse copy-control fixture");
25869        assert!(
25870            copy_control_tree.root_node().has_error(),
25871            "fixture must exercise adjacent copy-control recovery"
25872        );
25873        assert!(
25874            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
25875            "a complete late getter must remain recoverable after adjacent copy-control declarations"
25876        );
25877        let mut cursor = copy_control_tree.root_node().walk();
25878        assert!(
25879            copy_control_tree
25880                .root_node()
25881                .named_children(&mut cursor)
25882                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
25883            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
25884            copy_control_tree.root_node().to_sexp()
25885        );
25886        let constraint_tree = parser
25887            .parse(constraint_source, None)
25888            .expect("parse constraint-macro fixture");
25889        assert!(constraint_tree.root_node().has_error());
25890        assert!(
25891            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
25892            "complete constraint-macro members must not hide a later ordinary member"
25893        );
25894        let mut cursor = constraint_tree.root_node().walk();
25895        assert!(
25896            constraint_tree
25897                .root_node()
25898                .named_children(&mut cursor)
25899                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
25900                    child,
25901                    constraint_source
25902                )),
25903            "fixture must retain the split constraint-macro prefix/function geometry"
25904        );
25905    }
25906
25907    #[test]
25908    #[hardwon(
25909        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25910    )]
25911    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
25912        let source = r#"
25913struct Analyzer {
25914    struct Action {
25915        Action() = default;
25916        Action(const Action&) = default;
25917        Action& operator=(const Action& rhs) & = default;
25918
25919        template<class T,
25920                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
25921                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
25922        // NOLINTNEXTLINE(google-explicit-constructor)
25923        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
25924        {}
25925
25926        enum : std::uint16_t { None = 0, Read = (1 << 0) };
25927        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
25928
25929    private:
25930        unsigned int mFlag{};
25931    };
25932
25933    enum class Direction : unsigned char { Forward, Reverse };
25934    virtual Action analyze(Direction d) const = 0;
25935};
25936"#;
25937        let mut parser = tree_sitter::Parser::new();
25938        parser
25939            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25940            .unwrap();
25941        let tree = parser.parse(source, None).unwrap();
25942        assert!(tree.root_node().has_error());
25943        let root = tree.root_node();
25944        let outer = root
25945            .named_children(&mut root.walk())
25946            .find(|child| child.kind() == "ERROR")
25947            .expect("fragmented Analyzer prefix");
25948        let outer_recovered =
25949            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
25950        assert_eq!(outer_recovered.name, "Analyzer");
25951        let outer_tree = cpp_reparse_fragmented_class_body(
25952            source,
25953            outer_recovered.body.reparse_start,
25954            outer_recovered.body.reparse_end,
25955        )
25956        .expect("reparse Analyzer body");
25957        let outer_root = outer_tree.root_node();
25958        let action_prefix = outer_root
25959            .named_children(&mut outer_root.walk())
25960            .find(|child| child.kind() == "ERROR")
25961            .expect("fragmented Action prefix");
25962        let action_recovered = fragmented_class_body(action_prefix, source)
25963            .expect("structured Action fragment boundary");
25964        assert_eq!(action_recovered.name, "Action");
25965        let action_tree = cpp_reparse_fragmented_class_body(
25966            source,
25967            action_recovered.body.reparse_start,
25968            action_recovered.body.reparse_end,
25969        )
25970        .expect("reparse Action body");
25971        let action_root = action_tree.root_node();
25972        let macro_prefix = action_root
25973            .named_children(&mut action_root.walk())
25974            .find(|child| child.kind() == "ERROR")
25975            .expect("constraint macro prefix");
25976        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
25977            .expect("structured template macro prefix");
25978        let macro_companion =
25979            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
25980        assert!(
25981            cpp_reparsed_template_macro_constructor_companion_is_indexable(
25982                macro_companion,
25983                macro_parameter,
25984                source,
25985            ),
25986            "split constrained constructor must be admitted: {}",
25987            macro_companion.to_sexp()
25988        );
25989        assert!(
25990            cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
25991            "complete Action body must pass the recovery gate: {}",
25992            action_tree.root_node().to_sexp()
25993        );
25994        assert!(
25995            cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
25996            "complete Analyzer body must pass the recovery gate: {}",
25997            outer_tree.root_node().to_sexp()
25998        );
25999        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
26000        let parsed = parse_cpp_file(&file, source, &tree);
26001        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
26002            assert!(
26003                parsed
26004                    .declarations()
26005                    .iter()
26006                    .any(|unit| unit.fq_name() == expected),
26007                "missing recovered declaration {expected}: {:#?}",
26008                parsed.declarations()
26009            );
26010        }
26011        assert!(
26012            parsed
26013                .declarations()
26014                .iter()
26015                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
26016            "nested members must not remain flattened: {:#?}",
26017            parsed.declarations()
26018        );
26019    }
26020
26021    #[test]
26022    #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
26023    fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
26024        let source = "value() = default;\nint value();\n";
26025        let tree = crate::diagnostics::parse_cpp_tree(source);
26026        let root = tree.root_node();
26027        assert!(
26028            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
26029            "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
26030            root.to_sexp()
26031        );
26032        assert!(
26033            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
26034            "a return-typed same-name declaration is not a constructor witness: {}",
26035            root.to_sexp()
26036        );
26037
26038        let damaged_source = r#"
26039value(int x) {
26040  broken(
26041}
26042value() = default;
26043int value();
26044"#;
26045        let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
26046        let damaged_root = damaged_tree.root_node();
26047        // The macro-declaration grammar absorbs the whole damaged witness
26048        // into one errorful `function_definition`, so the default-assignment
26049        // expression lives inside the bogus body rather than as a top-level
26050        // sibling. The gate's answer must not change: the assignment is not
26051        // the class-named call, and the errorful same-name function is not a
26052        // separate constructor witness.
26053        let mut damaged_stack = vec![damaged_root];
26054        let mut damaged_assignment = None;
26055        while let Some(node) = damaged_stack.pop() {
26056            if node.kind() == "expression_statement" {
26057                damaged_assignment = Some(node);
26058                break;
26059            }
26060            let mut cursor = node.walk();
26061            damaged_stack.extend(node.named_children(&mut cursor));
26062        }
26063        let damaged_assignment =
26064            damaged_assignment.expect("damaged witness probe retains the assignment expression");
26065        let mut damaged_cursor = damaged_root.walk();
26066        let damaged_members = damaged_root
26067            .named_children(&mut damaged_cursor)
26068            .collect::<Vec<_>>();
26069        assert!(
26070            !cpp_reparsed_defaulted_constructor_is_indexable(
26071                damaged_assignment,
26072                &damaged_members,
26073                Some("value"),
26074                damaged_source,
26075            ),
26076            "an errorful same-name function must not witness a recovered defaulted constructor: {}",
26077            damaged_root.to_sexp()
26078        );
26079        assert!(
26080            !cpp_reparsed_members_are_indexable_for_class(
26081                damaged_root,
26082                damaged_source,
26083                Some("value")
26084            ),
26085            "the damaged witness must not pass the member gate either: {}",
26086            damaged_root.to_sexp()
26087        );
26088    }
26089
26090    #[test]
26091    #[hardwon(
26092        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26093    )]
26094    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
26095        let source = r#"
26096raw_hash_set& operator=(raw_hash_set&& that) {
26097  return move_assign(
26098      std::move(that),
26099      typename AllocTraits::propagate_on_container_move_assignment());
26100}
26101
26102iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
26103  return {};
26104}
26105
26106void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
26107
26108iterator insert(const_iterator hint, value_type&& value)
26109    ABSL_ATTRIBUTE_LIFETIME_BOUND {
26110  return {};
26111}
26112
26113friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
26114  return left.size() == right.size();
26115}
26116
26117static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
26118  return static_cast<slot_type*>(buffer);
26119}
26120
26121protected:
26122// Included-range recovery can attach this comment to the template prefix.
26123template <class K>
26124void AssertOnFind([[maybe_unused]] const K& key) {
26125  Check(key);
26126}
26127"#;
26128        let mut parser = tree_sitter::Parser::new();
26129        parser
26130            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26131            .unwrap();
26132        let tree = parser.parse(source, None).unwrap();
26133        assert!(
26134            tree.root_node().has_error(),
26135            "the fixture must exercise tree-sitter's errorful member shapes"
26136        );
26137        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
26138
26139        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
26140        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
26141        assert!(!cpp_reparsed_members_are_indexable(
26142            incomplete_tree.root_node(),
26143            incomplete_source
26144        ));
26145
26146        let outside_error_source = "int foo() stray_attribute {}\n";
26147        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
26148        assert!(outside_error_tree.root_node().has_error());
26149        assert!(!cpp_reparsed_members_are_indexable(
26150            outside_error_tree.root_node(),
26151            outside_error_source
26152        ));
26153
26154        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
26155        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26156        assert!(!cpp_reparsed_members_are_indexable(
26157            variable_initializer_tree.root_node(),
26158            variable_initializer_source
26159        ));
26160    }
26161
26162    #[test]
26163    #[hardwon(
26164        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26165    )]
26166    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
26167        let positive_source = r#"
26168std::pair<iterator, bool> insert(init_type&& value)
26169    ABSL_ATTRIBUTE_LIFETIME_BOUND
26170#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26171  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26172#endif
26173{
26174  return emplace(std::move(value));
26175}
26176"#;
26177        let mut parser = tree_sitter::Parser::new();
26178        parser
26179            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26180            .unwrap();
26181        let positive_tree = parser.parse(positive_source, None).unwrap();
26182        assert!(
26183            positive_tree.root_node().has_error(),
26184            "the fixture must exercise the split attribute/requires shape"
26185        );
26186        assert!(cpp_reparsed_members_are_indexable(
26187            positive_tree.root_node(),
26188            positive_source
26189        ));
26190
26191        let template_return_source = r#"
26192pair<int> insert(init_type&& value)
26193    ABSL_ATTRIBUTE_LIFETIME_BOUND
26194#if LANGUAGE_LEVEL >= 202002L
26195  requires(!Predicate<init_type>::value)
26196#endif
26197// Attributes and the function body may be separated by comments.
26198{
26199  return {};
26200}
26201"#;
26202        let template_return_tree = parser.parse(template_return_source, None).unwrap();
26203        assert!(
26204            cpp_reparsed_members_are_indexable(
26205                template_return_tree.root_node(),
26206                template_return_source
26207            ),
26208            "template-return attribute/requires tree: {}",
26209            template_return_tree.root_node().to_sexp()
26210        );
26211
26212        let no_body_source = r#"
26213std::pair<iterator, bool> insert(init_type&& value)
26214    ABSL_ATTRIBUTE_LIFETIME_BOUND
26215#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26216  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26217#endif
26218+ 0;
26219"#;
26220        let no_body_tree = parser.parse(no_body_source, None).unwrap();
26221        assert!(!cpp_reparsed_members_are_indexable(
26222            no_body_tree.root_node(),
26223            no_body_source
26224        ));
26225
26226        let extra_payload_source = r#"
26227pair<int> insert(init_type&& value)
26228    ABSL_ATTRIBUTE_LIFETIME_BOUND
26229#if LANGUAGE_LEVEL >= 202002L
26230  int unrelated;
26231  requires(Predicate<init_type>::value)
26232#endif
26233{
26234  return {};
26235}
26236"#;
26237        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26238        assert!(!cpp_reparsed_members_are_indexable(
26239            extra_payload_tree.root_node(),
26240            extra_payload_source
26241        ));
26242
26243        let variable_initializer_source = r#"
26244int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26245#if LANGUAGE_LEVEL >= 202002L
26246  requires(true)
26247#endif
26248{
26249  bad;
26250}
26251"#;
26252        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26253        assert!(!cpp_reparsed_members_are_indexable(
26254            variable_initializer_tree.root_node(),
26255            variable_initializer_source
26256        ));
26257    }
26258
26259    #[test]
26260    #[hardwon(
26261        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26262    )]
26263    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26264        let source = r#"namespace absl {
26265ABSL_NAMESPACE_BEGIN namespace container_internal {
26266
26267class raw_hash_set : public Base {
26268 public:
26269  using value_type = int;
26270
26271  template <class U,
26272            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26273  void insert(U value) { (void)value; }
26274
26275  struct InsertSlot {
26276    raw_hash_set& s;
26277  };
26278};
26279
26280}
26281ABSL_NAMESPACE_END
26282}"#;
26283        let mut parser = tree_sitter::Parser::new();
26284        parser
26285            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26286            .unwrap();
26287        let tree = parser.parse(source, None).unwrap();
26288        let root = tree.root_node();
26289        let outer_namespace = root
26290            .named_children(&mut root.walk())
26291            .find(|child| child.kind() == "namespace_definition")
26292            .expect("outer absl namespace");
26293        let declaration_list = outer_namespace
26294            .child_by_field_name("body")
26295            .expect("outer namespace body");
26296        let sentinel_function = declaration_list
26297            .named_children(&mut declaration_list.walk())
26298            .find(|child| child.kind() == "function_definition")
26299            .expect("malformed namespace sentinel function");
26300        let ancestry = ParentIndex::new(root);
26301        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26302            .expect("structured nested namespace sentinel");
26303        let fragmented =
26304            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26305                .expect("fragmented raw_hash_set class");
26306        assert_eq!(fragmented.class_node.kind(), "ERROR");
26307        assert_eq!(fragmented.name, "raw_hash_set");
26308        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26309
26310        let outer_scope =
26311            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26312        let mut outer_siblings = Vec::new();
26313        push_cpp_sentinel_sibling_classes(
26314            &mut outer_siblings,
26315            declaration_list,
26316            sentinel.function,
26317            &outer_scope,
26318            source,
26319            &ancestry,
26320        );
26321        let [outer_shadow] = outer_siblings.as_slice() else {
26322            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26323        };
26324        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26325        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26326
26327        let field = "    raw_hash_set& s;";
26328        let start = source.find(field).expect("InsertSlot field") + 4;
26329        let node = root
26330            .descendant_for_byte_range(start, start + "raw_hash_set".len())
26331            .expect("raw_hash_set type node");
26332        let recovered = cpp_sentinel_recovered_classes(root, source);
26333        let [deep_class] = recovered.as_slice() else {
26334            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26335        };
26336        assert_eq!(
26337            deep_class.namespace_scope_components,
26338            vec!["absl", "container_internal"]
26339        );
26340        assert_eq!(
26341            deep_class.scope_components,
26342            vec!["absl", "container_internal", "raw_hash_set"]
26343        );
26344        assert!(
26345            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26346                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26347        );
26348
26349        assert_eq!(
26350            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26351            Some(vec![
26352                "absl".to_string(),
26353                "container_internal".to_string(),
26354                "raw_hash_set".to_string(),
26355                "InsertSlot".to_string(),
26356            ])
26357        );
26358
26359        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26360        let parsed = parse_cpp_file(&file, source, &tree);
26361        let raw_hash_set = parsed
26362            .declarations()
26363            .iter()
26364            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26365            .expect("recovered raw_hash_set class");
26366        assert_eq!(
26367            raw_hash_set.fq_name(),
26368            "absl::container_internal.raw_hash_set",
26369            "the recovered declaration must publish under the deeper sentinel namespace"
26370        );
26371        assert_eq!(
26372            parsed.raw_supertypes.get(raw_hash_set),
26373            Some(&vec!["Base".to_string()]),
26374            "the structured base clause on the fragmented ERROR prefix must survive publication"
26375        );
26376        assert!(
26377            parsed.materialization_records.iter().any(|record| matches!(
26378                record,
26379                MaterializationRecord::RecoveredDeclaration { recovery, unit }
26380                    if unit == raw_hash_set && *recovery == deep_class.class_range
26381            )),
26382            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26383            parsed.materialization_records
26384        );
26385    }
26386
26387    /// Issue #2358: recording an aggregate definition must not walk the whole
26388    /// file.
26389    ///
26390    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
26391    /// class-like shape that has a body, so the removal step runs once per
26392    /// aggregate. It used to `retain` over `top_level_declarations` and over
26393    /// *every* child list in the file on each of those calls, comparing whole
26394    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
26395    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
26396    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
26397    /// tens of thousands of times over and the C forward differential never
26398    /// finished.
26399    ///
26400    /// A definition the file has not already declared removes nothing, so the
26401    /// honest cost is zero regardless of how many other aggregates surround it.
26402    /// Two sizes an order of magnitude apart pin that the count is not merely
26403    /// small but independent of the file.
26404    ///
26405    /// The declaration walk answers every ancestor question from a
26406    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
26407    /// the root for each one (#2361). Substituting the index is only safe
26408    /// because it answers the identical question, so pin that on the shapes
26409    /// this file's recovery paths care about: anonymous and named aggregates,
26410    /// nested namespaces, templates, macro-displaced declarations and the
26411    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
26412    /// too -- `Node::parent` walks the visible tree, not the named one.
26413    #[test]
26414    #[hardwon(
26415        "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
26416    )]
26417    fn the_parent_index_answers_what_tree_sitter_answers() {
26418        const SHAPES: [&str; 5] = [
26419            "namespace outer { namespace inner { struct Tag { int field; }; } }",
26420            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
26421            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
26422            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
26423            "class API Broken : public First, public Second {\n  void member();\n",
26424        ];
26425        for source in SHAPES {
26426            let mut parser = tree_sitter::Parser::new();
26427            parser
26428                .set_language(&tree_sitter_cpp::LANGUAGE.into())
26429                .unwrap();
26430            let tree = parser.parse(source, None).unwrap();
26431            let root = tree.root_node();
26432            let ancestry = ParentIndex::new(root);
26433            let mut nodes = 0usize;
26434            let mut stack = vec![root];
26435            while let Some(node) = stack.pop() {
26436                nodes += 1;
26437                assert_eq!(
26438                    node.parent().map(|parent| parent.id()),
26439                    ancestry.parent(node).map(|parent| parent.id()),
26440                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
26441                );
26442                let mut cursor = node.walk();
26443                stack.extend(node.children(&mut cursor));
26444            }
26445            assert!(nodes > 1, "{source:?} produced no tree to compare");
26446        }
26447    }
26448
26449    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
26450    /// index for every ancestor edge. Asking tree-sitter directly makes each
26451    /// edge re-descend from the root, turning declaration extraction on a
26452    /// deeply nested generated header from quadratic output work into a cubic
26453    /// tree walk. Exact query counts pin the route without a machine-dependent
26454    /// wall-clock ceiling.
26455    #[test]
26456    #[hardwon(
26457        "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
26458    )]
26459    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
26460        const DEPTH: usize = 64;
26461        let mut source = String::new();
26462        for level in 0..DEPTH {
26463            writeln!(source, "namespace n{level} {{").unwrap();
26464        }
26465        source.push_str("int deepest(int value);\n");
26466        for _ in 0..DEPTH {
26467            source.push_str("}\n");
26468        }
26469
26470        let mut parser = tree_sitter::Parser::new();
26471        parser
26472            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26473            .unwrap();
26474        let tree = parser.parse(&source, None).unwrap();
26475        let root = tree.root_node();
26476        let ancestry = ParentIndex::new(root);
26477        let mut function_declarator = None;
26478        walk_named_tree_preorder(root, true, |node| {
26479            if node.kind() == "function_declarator" {
26480                function_declarator = Some(node);
26481                WalkControl::Break
26482            } else {
26483                WalkControl::Continue
26484            }
26485        });
26486        let function_declarator = function_declarator.expect("deepest function declarator");
26487        let ancestor_count =
26488            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
26489
26490        ancestry.reset_parent_query_count_for_test();
26491        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
26492        assert_eq!(DEPTH, lexical_scope.len());
26493        assert_eq!(
26494            ancestor_count + 1,
26495            ancestry.parent_query_count_for_test(),
26496            "lexical-scope ancestry bypassed the parent index"
26497        );
26498
26499        ancestry.reset_parent_query_count_for_test();
26500        assert_eq!(
26501            Some(DispatchExtensibility::Closed),
26502            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
26503        );
26504        assert_eq!(
26505            ancestor_count,
26506            ancestry.parent_query_count_for_test(),
26507            "dispatch ancestry bypassed the parent index"
26508        );
26509
26510        ancestry.reset_parent_query_count_for_test();
26511        assert_eq!(
26512            CallableLinkage::External,
26513            cpp_callable_linkage(function_declarator, &source, &ancestry)
26514        );
26515        assert_eq!(
26516            ancestor_count + 1,
26517            ancestry.parent_query_count_for_test(),
26518            "linkage ancestry bypassed the parent index"
26519        );
26520
26521        ancestry.reset_parent_query_count_for_test();
26522        assert!(!cpp_callable_is_structural_constructor(
26523            function_declarator,
26524            &source,
26525            &ancestry
26526        ));
26527        assert_eq!(
26528            ancestor_count + 1,
26529            ancestry.parent_query_count_for_test(),
26530            "constructor ancestry bypassed the parent index"
26531        );
26532    }
26533
26534    /// #3507: the one row-level dispatch fact C++ can state is stated only
26535    /// where the language states it -- in the class body.
26536    ///
26537    /// A call's exactness is not readable from a definition row: an
26538    /// out-of-line definition repeats no `virtual`, no `final`, and no base
26539    /// clause, and C++ inherits virtual-ness from a base that declares the
26540    /// member. Each shape below therefore has to answer for itself, and only
26541    /// a class body with no base clause at all can close a member that writes
26542    /// nothing.
26543    #[test]
26544    #[hardwon(
26545        "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
26546    )]
26547    fn callable_dispatch_extensibility_comes_from_the_class_body() {
26548        const SOURCE: &str = "\
26549class Plain { public: long send(long order); };
26550class Derived : public Base { public: long send(long order); };
26551class Sealed : public Base { public: long send(long order) final; };
26552class Hiding : public Base { public: long send(long order) { return order; } };
26553class Virtual { public: virtual long send(long order); };
26554namespace shop { long free_call(long order); }
26555long Plain::send(long order) { return order; }
26556";
26557        let mut parser = tree_sitter::Parser::new();
26558        parser
26559            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26560            .unwrap();
26561        let tree = parser.parse(SOURCE, None).unwrap();
26562        let root = tree.root_node();
26563        let ancestry = ParentIndex::new(root);
26564        let mut answers = Vec::new();
26565        walk_named_tree_preorder(root, true, |node| {
26566            if node.kind() != "function_declarator" {
26567                return WalkControl::Continue;
26568            }
26569            let name = cpp_function_declarator_name_node(node).expect("declarator name");
26570            answers.push((
26571                node_text(name, SOURCE).to_string(),
26572                cpp_callable_dispatch_extensibility(node, &ancestry),
26573            ));
26574            WalkControl::Continue
26575        });
26576        assert_eq!(
26577            answers,
26578            vec![
26579                ("send".to_string(), Some(DispatchExtensibility::Closed)),
26580                ("send".to_string(), Some(DispatchExtensibility::Open)),
26581                ("send".to_string(), Some(DispatchExtensibility::Closed)),
26582                ("send".to_string(), Some(DispatchExtensibility::Open)),
26583                ("send".to_string(), Some(DispatchExtensibility::Open)),
26584                ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
26585                ("Plain::send".to_string(), None),
26586            ],
26587            "the class body states the fact; a qualified definition states none"
26588        );
26589    }
26590
26591    #[test]
26592    #[hardwon(
26593        "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
26594    )]
26595    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
26596        const DISTINCT_PER_KIND: usize = 64;
26597        let mut source = String::new();
26598        for index in 0..DISTINCT_PER_KIND {
26599            writeln!(source, "typedef int Alias{index};").unwrap();
26600        }
26601        writeln!(source, "typedef long Alias0;").unwrap();
26602        for index in 0..DISTINCT_PER_KIND {
26603            writeln!(source, "#define MACRO_{index} {index}").unwrap();
26604        }
26605        writeln!(source, "#define MACRO_0 duplicate").unwrap();
26606        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
26607
26608        let mut parser = tree_sitter::Parser::new();
26609        parser
26610            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26611            .unwrap();
26612        let tree = parser.parse(&source, None).unwrap();
26613        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
26614
26615        start_declaration_identity_comparison_probe();
26616        let parsed = parse_cpp_file(&file, &source, &tree);
26617        let comparisons = finish_declaration_identity_comparison_probe();
26618
26619        assert_eq!(
26620            DISTINCT_PER_KIND + 1,
26621            parsed
26622                .declarations()
26623                .iter()
26624                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
26625                .count(),
26626            "every physical typedef alias declaration must be retained so \
26627             conditional branch guards stay available to the resolver"
26628        );
26629        assert_eq!(
26630            DISTINCT_PER_KIND + 1,
26631            parsed
26632                .declarations()
26633                .iter()
26634                .filter(|unit| {
26635                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
26636                })
26637                .count(),
26638            "distinct macro redefinitions must remain available to temporal lookup"
26639        );
26640        assert_eq!(
26641            2,
26642            parsed
26643                .declarations()
26644                .iter()
26645                .filter(|unit| {
26646                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
26647                })
26648                .count(),
26649            "function overloads must remain distinct"
26650        );
26651
26652        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
26653        assert!(
26654            comparisons <= dedup_inputs * 4,
26655            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
26656        );
26657    }
26658
26659    #[test]
26660    #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
26661    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
26662        let negative_source = r#"namespace absl {
26663ABSL_NAMESPACE_BEGIN namespace container_internal {
26664template <typename T>
26665class broken {
26666 public:
26667  using value_type = T;
26668  T operator->() const { return &operator*(); }
26669}
26670}
26671"#;
26672        let mut parser = tree_sitter::Parser::new();
26673        parser
26674            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26675            .unwrap();
26676        let negative_tree = parser.parse(negative_source, None).unwrap();
26677        let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
26678            .expect("the negative fixture must expose the malformed class node");
26679        assert!(
26680            broken.has_error(),
26681            "the negative fixture must retain a parser error"
26682        );
26683        // The macro-declaration grammar keeps the class body closed here; the
26684        // damage is the missing `;` and the unclosed namespace around it, so
26685        // the class's incompleteness is proven by the sentinel recognizers
26686        // finding no envelope for it rather than by a shared parent boundary.
26687        let mut envelope = None;
26688        {
26689            let mut stack = vec![negative_tree.root_node()];
26690            while let Some(node) = stack.pop() {
26691                if cpp_nested_namespace_sentinel(
26692                    node,
26693                    negative_source,
26694                    &ParentIndex::new(negative_tree.root_node()),
26695                )
26696                .is_some()
26697                    || cpp_root_namespace_sentinel(
26698                        node,
26699                        negative_source,
26700                        &ParentIndex::new(negative_tree.root_node()),
26701                    )
26702                    .is_some()
26703                    || cpp_sentinel_macro_class_region(node, negative_source).is_some()
26704                {
26705                    envelope = Some(node);
26706                    break;
26707                }
26708                stack.extend(node.named_children(&mut node.walk()));
26709            }
26710        }
26711        assert!(
26712            envelope.is_none(),
26713            "an incomplete class must not prove a sentinel envelope: {}",
26714            negative_tree.root_node().to_sexp()
26715        );
26716        let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
26717        assert!(
26718            recovered
26719                .iter()
26720                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
26721            "an incomplete class must not borrow the namespace close: {recovered:#?}"
26722        );
26723
26724        let positive_source = r#"namespace absl {
26725ABSL_NAMESPACE_BEGIN namespace container_internal {
26726template <typename T>
26727class broken {
26728 public:
26729  using value_type = T;
26730  T operator->() const { return &operator*(); }
26731  using alias = value_type;
26732};
26733}
26734}
26735"#;
26736        let positive_tree = parser.parse(positive_source, None).unwrap();
26737        let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
26738            .expect("the positive fixture must expose the broken class node");
26739        assert!(
26740            broken.has_error(),
26741            "the positive fixture must retain an internal parser error"
26742        );
26743        assert!(
26744            cpp_complete_class_body_close(broken).is_some(),
26745            "the positive fixture must expose a real class body close"
26746        );
26747        let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
26748        assert!(
26749            recovered.iter().any(|class| {
26750                class.scope_components == ["absl", "container_internal", "broken"]
26751            }),
26752            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
26753        );
26754    }
26755
26756    #[test]
26757    #[hardwon(
26758        "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
26759    )]
26760    fn sentinel_recovery_keeps_members_after_nested_body_close() {
26761        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
26762NLOHMANN_BASIC_JSON_TPL_DECLARATION
26763class basic_json {
26764 private:
26765  union storage {
26766    int value;
26767  } data;
26768 public:
26769  using late_alias = int;
26770  late_alias value() const;
26771};
26772NLOHMANN_JSON_NAMESPACE_END
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 recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26780        let basic_json = recovered
26781            .iter()
26782            .find(|class| {
26783                class
26784                    .scope_components
26785                    .last()
26786                    .is_some_and(|name| name == "basic_json")
26787            })
26788            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
26789        let late_alias = source
26790            .find("late_alias value")
26791            .expect("late alias reference");
26792        assert!(
26793            basic_json.class_range.start_byte < late_alias
26794                && late_alias < basic_json.class_range.end_byte,
26795            "the recovered class range must include members after a nested close: {basic_json:#?}"
26796        );
26797    }
26798
26799    #[test]
26800    #[hardwon(
26801        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
26802    )]
26803    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
26804        let source = r#"namespace absl {
26805ABSL_NAMESPACE_BEGIN namespace container_internal {
26806template <typename T>
26807struct broken {
26808  using value_type = T;
26809};
26810}
26811
26812#ifdef OWNER_DEF
26813template <typename T>
26814typename broken<T>::value_type broken<T>::method() {
26815  value_type value{};
26816  return value;
26817}
26818#endif
26819
26820namespace sibling {
26821template <typename T>
26822typename broken<T>::value_type broken<T>::other() {
26823  value_type value{};
26824  return value;
26825}
26826}
26827
26828ABSL_NAMESPACE_END
26829}
26830"#;
26831        let mut parser = tree_sitter::Parser::new();
26832        parser
26833            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26834            .unwrap();
26835        let tree = parser.parse(source, None).unwrap();
26836        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26837        let broken = recovered
26838            .iter()
26839            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
26840            .expect("the sentinel class must be recovered");
26841        let method_start = source
26842            .find("typename broken<T>::value_type broken<T>::method()")
26843            .expect("guarded sibling owner");
26844        let method_end = source[method_start..]
26845            .find("\n}")
26846            .map(|offset| method_start + offset + 2)
26847            .expect("guarded sibling owner close");
26848        assert!(
26849            broken
26850                .owner_ranges
26851                .iter()
26852                .any(|owner| owner.range.start_byte <= method_start
26853                    && method_end <= owner.range.end_byte),
26854            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
26855        );
26856        let sibling_start = source
26857            .find("typename broken<T>::value_type broken<T>::other()")
26858            .expect("nested namespace sibling owner");
26859        assert!(
26860            broken
26861                .owner_ranges
26862                .iter()
26863                .all(|owner| owner.range.start_byte > sibling_start
26864                    || owner.range.end_byte <= sibling_start),
26865            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
26866        );
26867    }
26868
26869    #[test]
26870    #[hardwon(
26871        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
26872    )]
26873    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
26874        let source = r#"#ifdef OUTER
26875namespace absl {
26876ABSL_NAMESPACE_BEGIN namespace container_internal {
26877template <typename T>
26878struct broken {
26879  using value_type = T;
26880};
26881}
26882}
26883
26884#ifdef OWNER_DEF
26885template <typename T>
26886typename broken<T>::value_type broken<T>::method() {
26887  value_type value{};
26888  return value;
26889}
26890#endif
26891#endif
26892"#;
26893        // The macro-declaration grammar keeps the sentinel envelope clean, so
26894        // the recovered-region index has nothing malformed to repair; the
26895        // declaration walk owns the recovery, and the out-of-line definition
26896        // after the missing ABSL_NAMESPACE_END must stay unowned.
26897        let parsed = parse_cpp_declarations(source, "discard-outer.h");
26898        assert!(
26899            parsed
26900                .declarations()
26901                .iter()
26902                .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
26903            "the sentinel class must be recovered: {:#?}",
26904            parsed.declarations()
26905        );
26906        let method = parsed
26907            .declarations()
26908            .iter()
26909            .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
26910            .expect("the guarded out-of-line definition");
26911        assert_eq!(
26912            method.fq_name(),
26913            "broken.method",
26914            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
26915            parsed.declarations()
26916        );
26917    }
26918
26919    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
26920    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
26921        let mut signatures = all_function_identities(parsed)
26922            .into_iter()
26923            .filter(|(name, _)| name == fq_name)
26924            .filter_map(|(_, signature)| signature)
26925            .collect::<Vec<_>>();
26926        signatures.sort();
26927        signatures.dedup();
26928        signatures
26929    }
26930
26931    #[test]
26932    #[hardwon(
26933        "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
26934    )]
26935    fn callable_parameter_types_come_from_the_ast_parameter_list() {
26936        let source = r#"
26937template <typename T, ENABLE_BYTES(T)>
26938Vec256<T> DupOdd(Vec256<T> value) { return value; }
26939
26940struct Visitor {
26941  void fail(this auto const& self) {}
26942  void consume(this Visitor const& self, int count) {}
26943};
26944"#;
26945        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
26946        let dup_odd = parsed
26947            .declarations()
26948            .iter()
26949            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
26950            .expect("DupOdd declaration");
26951        assert_eq!(
26952            dup_odd.signature(),
26953            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
26954        );
26955        assert_eq!(
26956            parsed
26957                .signature_metadata
26958                .get(dup_odd)
26959                .and_then(|metadata| metadata.first())
26960                .and_then(SignatureMetadata::callable_parameter_types),
26961            Some(["Vec256<T>".to_string()].as_slice())
26962        );
26963
26964        let fail = parsed
26965            .declarations()
26966            .iter()
26967            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
26968            .expect("explicit-object member");
26969        assert_eq!(fail.signature(), Some("(this const auto &)"));
26970        let metadata = parsed
26971            .signature_metadata
26972            .get(fail)
26973            .and_then(|metadata| metadata.first())
26974            .expect("explicit-object signature metadata");
26975        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
26976        assert!(
26977            metadata
26978                .callable_arity()
26979                .is_some_and(|arity| arity.accepts(0))
26980        );
26981        let consume = parsed
26982            .declarations()
26983            .iter()
26984            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
26985            .expect("explicit-object member with an invocation argument");
26986        assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
26987        let metadata = parsed
26988            .signature_metadata
26989            .get(consume)
26990            .and_then(|metadata| metadata.first())
26991            .expect("typed explicit-object signature metadata");
26992        assert_eq!(
26993            metadata.callable_parameter_types(),
26994            Some(["int".to_string()].as_slice())
26995        );
26996        assert!(
26997            metadata
26998                .callable_arity()
26999                .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
27000        );
27001    }
27002
27003    #[test]
27004    #[hardwon(
27005        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
27006    )]
27007    fn trailing_qualifiers_survive_parameter_list_whitespace() {
27008        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
27009        // declarator's structure, so an out-of-line definition that spells its
27010        // parameter list with different whitespace than the declaration must
27011        // still carry it.
27012        let source = r#"
27013struct Widget {
27014  bool multiline(int settings, int supprs) const;
27015  bool doublespace(int settings, int supprs) const;
27016  bool noexcept_multiline(int settings, int supprs) noexcept;
27017  bool ref_multiline(int settings, int supprs) &&;
27018};
27019bool
27020Widget::multiline (int settings,
27021                   int supprs) const
27022{ return settings + supprs > 0; }
27023bool Widget::doublespace(int settings,  int supprs) const { return true; }
27024bool Widget::noexcept_multiline(int settings,
27025                                int supprs) noexcept { return true; }
27026bool Widget::ref_multiline(int settings,
27027                           int supprs) && { return true; }
27028"#;
27029        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
27030        assert_eq!(
27031            vec!["(int, int) const".to_string()],
27032            identity_signatures(&parsed, "Widget.multiline")
27033        );
27034        assert_eq!(
27035            vec!["(int, int) const".to_string()],
27036            identity_signatures(&parsed, "Widget.doublespace")
27037        );
27038        assert_eq!(
27039            vec!["(int, int) noexcept".to_string()],
27040            identity_signatures(&parsed, "Widget.noexcept_multiline")
27041        );
27042        assert_eq!(
27043            vec!["(int, int) &&".to_string()],
27044            identity_signatures(&parsed, "Widget.ref_multiline")
27045        );
27046    }
27047
27048    #[test]
27049    #[hardwon(
27050        "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27051    )]
27052    fn macro_fragmented_plain_class_keeps_following_member_signature() {
27053        let source = r#"
27054struct CString {};
27055class CMessage {
27056public:
27057  CString GetParams(unsigned int index, unsigned int length = -1) const
27058      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
27059    return GetParamsColon(index, length);
27060  }
27061  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
27062};
27063CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
27064  return {};
27065}
27066"#;
27067        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
27068        assert_eq!(
27069            vec!["(unsigned int, unsigned int) const".to_string()],
27070            identity_signatures(&parsed, "CMessage.GetParamsColon")
27071        );
27072    }
27073
27074    #[test]
27075    #[hardwon(
27076        "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
27077    )]
27078    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
27079        let source = r#"
27080#pragma once
27081#define DEMO_DEPRECATED(message)
27082namespace demo {
27083struct Base {
27084    static int aligned(int value) { return value; }
27085    int legacy(int value) const
27086        DEMO_DEPRECATED("use replacement()") { return value; }
27087    int replacement() const;
27088    void run(int value);
27089};
27090struct OtherBase {
27091    void run(int value);
27092    static int aligned(int value) { return value; }
27093};
27094struct Derived : Base {};
27095struct Override : Base {
27096    void run(int value);
27097    static int aligned(int value) { return value; }
27098};
27099struct RecoveredOverride : Base {
27100    int legacy(int value) const
27101        DEMO_DEPRECATED("use replacement()") { return value; }
27102    void run(int value);
27103};
27104struct Hidden : Base {
27105    void run(int first, int second);
27106    static int aligned(int first, int second) { return first + second; }
27107};
27108struct Ambiguous : Base, OtherBase {};
27109}
27110struct Global {};
27111"#;
27112        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
27113        let declarations = parsed.declarations();
27114        let fq_names = declarations
27115            .iter()
27116            .map(|unit| unit.fq_name())
27117            .collect::<std::collections::BTreeSet<_>>();
27118
27119        for expected in [
27120            "demo.Base",
27121            "demo.Base.aligned",
27122            "demo.Base.legacy",
27123            "demo.Base.replacement",
27124            "demo.Base.run",
27125            "demo.Derived",
27126            "demo.OtherBase",
27127            "demo.Override",
27128            "demo.RecoveredOverride",
27129            "demo.Hidden",
27130            "demo.Ambiguous",
27131            "Global",
27132        ] {
27133            assert!(
27134                fq_names.contains(expected),
27135                "missing {expected} from namespaced macro fragment: {declarations:#?}"
27136            );
27137        }
27138        assert!(
27139            !fq_names.contains("Derived"),
27140            "following class escaped its namespace: {declarations:#?}"
27141        );
27142        assert!(
27143            !fq_names.contains("demo.Global"),
27144            "global class crossed the recovered namespace boundary: {declarations:#?}"
27145        );
27146    }
27147
27148    #[test]
27149    #[hardwon(
27150        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
27151    )]
27152    fn trailing_qualifiers_still_separate_genuine_overloads() {
27153        // The qualifier must keep distinguishing the real C++ overload sets it
27154        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
27155        let source = r#"
27156struct Widget {
27157  int* slot(int index);
27158  const int* slot(int index) const;
27159  int log(int severity) &;
27160  int log(int severity) &&;
27161};
27162"#;
27163        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
27164        assert_eq!(
27165            vec!["(int)".to_string(), "(int) const".to_string()],
27166            identity_signatures(&parsed, "Widget.slot")
27167        );
27168        assert_eq!(
27169            vec!["(int) &".to_string(), "(int) &&".to_string()],
27170            identity_signatures(&parsed, "Widget.log")
27171        );
27172    }
27173
27174    #[test]
27175    #[hardwon(
27176        "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
27177    )]
27178    fn virtual_specifier_is_not_part_of_the_identity_signature() {
27179        // `override` never appears on the out-of-line definition, and C++ does
27180        // not make it part of the signature, so it must not split the identity.
27181        let source = r#"
27182struct Base {
27183  virtual void run(int value) const;
27184};
27185struct Widget : Base {
27186  void run(int value) const override;
27187};
27188void Widget::run(int value) const {}
27189"#;
27190        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
27191        assert_eq!(
27192            vec!["(int) const".to_string()],
27193            identity_signatures(&parsed, "Widget.run")
27194        );
27195    }
27196
27197    #[test]
27198    #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
27199    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
27200        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
27201        // the function type, so a declaration that spells `const int` and a
27202        // definition that spells `int` are one entity.
27203        let source = r#"
27204struct Widget {
27205  bool value_params(const int settings, const int supprs);
27206  void pointee_const(const int* p);
27207  void pointer_const(int* const p);
27208  void both_const(const int* const p);
27209  void reference_const(const int& p);
27210  void array_const(const int values[4]);
27211  void take(const int* p);
27212  void take(int* p);
27213};
27214bool Widget::value_params(int settings, int supprs) { return true; }
27215void Widget::pointer_const(int* p) {}
27216void Widget::both_const(const int* p) {}
27217"#;
27218        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27219        assert_eq!(
27220            vec!["(int, int)".to_string()],
27221            identity_signatures(&parsed, "Widget.value_params")
27222        );
27223        assert_eq!(
27224            vec!["(int *)".to_string()],
27225            identity_signatures(&parsed, "Widget.pointer_const")
27226        );
27227        assert_eq!(
27228            vec!["(const int *)".to_string()],
27229            identity_signatures(&parsed, "Widget.both_const")
27230        );
27231        // The const that is not top-level still distinguishes the type.
27232        assert_eq!(
27233            vec!["(const int *)".to_string()],
27234            identity_signatures(&parsed, "Widget.pointee_const")
27235        );
27236        assert_eq!(
27237            vec!["(const int &)".to_string()],
27238            identity_signatures(&parsed, "Widget.reference_const")
27239        );
27240        assert_eq!(
27241            vec!["(const int [4])".to_string()],
27242            identity_signatures(&parsed, "Widget.array_const")
27243        );
27244        assert_eq!(
27245            vec!["(const int *)".to_string(), "(int *)".to_string()],
27246            identity_signatures(&parsed, "Widget.take")
27247        );
27248    }
27249
27250    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27251        let mut parser = tree_sitter::Parser::new();
27252        parser
27253            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27254            .unwrap();
27255        let tree = parser.parse(source, None).unwrap();
27256        let start = source.find(callable_name).expect("callable declaration");
27257        let declarator =
27258            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27259        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27260    }
27261
27262    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27263        let mut shapes = comparable_shapes(source, callable_name);
27264        assert_eq!(1, shapes.len(), "{shapes:?}");
27265        match shapes.remove(0) {
27266            CppComparableSlot::Shape(shape) => shape,
27267            other => panic!("expected a comparable shape, got {other:?}"),
27268        }
27269    }
27270
27271    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27272        let mut current = shape.root();
27273        loop {
27274            match shape.node(current) {
27275                CppComparableNode::Named { .. } => return shape.node(current),
27276                CppComparableNode::Pointer { inner, .. }
27277                | CppComparableNode::Reference { inner }
27278                | CppComparableNode::Array { inner } => current = *inner,
27279                CppComparableNode::Generic { base, .. } => current = *base,
27280            }
27281        }
27282    }
27283
27284    #[test]
27285    #[hardwon(
27286        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27287    )]
27288    fn comparable_shape_keeps_pointee_const() {
27289        assert_ne!(
27290            sole_comparable_shape("void f(const char* p);", "f("),
27291            sole_comparable_shape("void f(char* p);", "f(")
27292        );
27293    }
27294
27295    #[test]
27296    #[hardwon(
27297        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27298    )]
27299    fn comparable_shape_keeps_inner_pointer_const() {
27300        assert_ne!(
27301            sole_comparable_shape("void f(int** p);", "f("),
27302            sole_comparable_shape("void f(int* const* p);", "f(")
27303        );
27304    }
27305
27306    #[test]
27307    #[hardwon(
27308        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27309    )]
27310    fn comparable_shape_drops_top_level_pointer_const() {
27311        assert_eq!(
27312            sole_comparable_shape("void f(int* const p);", "f("),
27313            sole_comparable_shape("void f(int* p);", "f(")
27314        );
27315    }
27316
27317    #[test]
27318    #[hardwon(
27319        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27320    )]
27321    fn comparable_shape_drops_top_level_base_const() {
27322        assert_eq!(
27323            sole_comparable_shape("void f(const int p);", "f("),
27324            sole_comparable_shape("void f(int p);", "f(")
27325        );
27326    }
27327
27328    #[test]
27329    #[hardwon(
27330        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27331    )]
27332    fn comparable_shape_decays_top_level_array_to_pointer() {
27333        assert_eq!(
27334            sole_comparable_shape("void f(int a[3]);", "f("),
27335            sole_comparable_shape("void f(int* a);", "f(")
27336        );
27337        assert_eq!(
27338            sole_comparable_shape("void f(int* a[3]);", "f("),
27339            sole_comparable_shape("void f(int** a);", "f(")
27340        );
27341    }
27342
27343    #[test]
27344    #[hardwon(
27345        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27346    )]
27347    fn comparable_shape_keeps_array_behind_pointer() {
27348        assert_ne!(
27349            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27350            sole_comparable_shape("struct S { void f(int** a); };", "f(")
27351        );
27352    }
27353
27354    #[test]
27355    #[hardwon(
27356        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27357    )]
27358    fn comparable_shape_records_written_name_and_lexical_scope() {
27359        let declared =
27360            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27361        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27362        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27363            panic!("named leaf");
27364        };
27365        assert_eq!(["Msg".to_string()].as_slice(), name.path());
27366        assert_eq!(
27367            ["ns".to_string(), "S".to_string()].as_slice(),
27368            name.lexical_scope()
27369        );
27370        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27371            panic!("named leaf");
27372        };
27373        assert_eq!(
27374            ["ns".to_string(), "Msg".to_string()].as_slice(),
27375            name.path()
27376        );
27377        assert!(name.lexical_scope().is_empty());
27378        assert_ne!(declared, defined);
27379    }
27380
27381    #[test]
27382    #[hardwon(
27383        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27384    )]
27385    fn comparable_shape_marks_sized_primitive_leaf() {
27386        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27387        let CppComparableNode::Named {
27388            name, primitive, ..
27389        } = comparable_named_leaf(&shape)
27390        else {
27391            panic!("named leaf");
27392        };
27393        assert!(primitive);
27394        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
27395        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
27396    }
27397
27398    #[test]
27399    #[hardwon(
27400        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
27401    )]
27402    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
27403        assert_eq!(
27404            vec![CppComparableSlot::Unstructured],
27405            comparable_shapes("void f(void (*cb)(int));", "f(")
27406        );
27407    }
27408
27409    #[test]
27410    #[hardwon(
27411        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
27412    )]
27413    fn comparable_shape_reports_ellipsis_slot() {
27414        let shapes = comparable_shapes("void f(int a, ...);", "f(");
27415        assert_eq!(2, shapes.len(), "{shapes:?}");
27416        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
27417    }
27418
27419    #[test]
27420    #[hardwon(
27421        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
27422    )]
27423    fn comparable_shape_keeps_template_argument_const() {
27424        assert_ne!(
27425            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
27426            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
27427        );
27428    }
27429
27430    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
27431    /// file-scope tag that a later `struct inner *` at file scope may name.
27432    #[test]
27433    #[hardwon(
27434        "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
27435    )]
27436    fn c_file_mints_aggregate_member_tag_at_file_scope() {
27437        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27438        let parsed = parse_cpp_declarations(source, "x.c");
27439        let declarations = parsed.declarations();
27440
27441        assert!(
27442            declarations
27443                .iter()
27444                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27445            "expected a file-scope inner tag, got {declarations:?}"
27446        );
27447        assert!(
27448            declarations
27449                .iter()
27450                .all(|unit| unit.fq_name() != "outer$inner"),
27451            "expected no nested identity, got {declarations:?}"
27452        );
27453        assert!(
27454            declarations
27455                .iter()
27456                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
27457        );
27458        // Members still belong to their own aggregate.
27459        assert!(
27460            declarations
27461                .iter()
27462                .any(|unit| unit.fq_name() == "inner.value")
27463        );
27464        assert!(
27465            declarations
27466                .iter()
27467                .any(|unit| unit.fq_name() == "outer.item")
27468        );
27469
27470        let outer = declarations
27471            .iter()
27472            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
27473            .expect("outer");
27474        assert!(
27475            parsed
27476                .children
27477                .get(outer)
27478                .into_iter()
27479                .flatten()
27480                .all(|child| child.fq_name() != "inner"),
27481            "the tag must not hang off the aggregate it is written inside: {:?}",
27482            parsed.children
27483        );
27484    }
27485
27486    /// A header carries no compilation language of its own, and a `.cpp`
27487    /// translation unit really does declare a nested class. Both keep exactly
27488    /// the C++ extraction they had before the C dialect existed.
27489    #[test]
27490    #[hardwon(
27491        "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
27492    )]
27493    fn header_and_cpp_files_keep_nested_tag_identity() {
27494        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27495        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
27496            let parsed = parse_cpp_declarations(source, name);
27497            let declarations = parsed.declarations();
27498            assert!(
27499                declarations
27500                    .iter()
27501                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27502                "{name} must keep the nested identity, got {declarations:?}"
27503            );
27504            assert!(
27505                declarations.iter().all(|unit| unit.fq_name() != "inner"),
27506                "{name} must not mint a file-scope tag, got {declarations:?}"
27507            );
27508            assert!(
27509                declarations
27510                    .iter()
27511                    .any(|unit| unit.fq_name() == "outer$inner.value")
27512            );
27513        }
27514    }
27515
27516    #[test]
27517    fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
27518        let source = r#"
27519template <class K, class V, class Traits = CacheTraits<K>>
27520class ARCCache {
27521 public:
27522    struct ArcSizeInfo {
27523        void BSize() {}
27524    };
27525 private:
27526    struct BMapVal;
27527    struct BListVal;
27528    typedef std::unordered_map<K, BMapVal> BMap;
27529    typedef typename BMap::iterator bmap_iter;
27530    typedef typename std::list<BListVal> BList;
27531    typedef typename BList::iterator blist_iter;
27532    struct BListVal {
27533        bmap_iter map_iter;
27534        BListVal() = default;
27535    };
27536    struct DefinedVal {
27537        DefinedVal() {}
27538    };
27539};
27540"#;
27541        let tree = crate::diagnostics::parse_cpp_tree(source);
27542        let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
27543        let cpp = crate::adapter::parse_cpp_file_in_dialect(
27544            &file,
27545            source,
27546            &tree,
27547            LanguageDialect::Standard(Language::Cpp),
27548        );
27549        let c =
27550            crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
27551        for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
27552            for fq_name in [
27553                "ARCCache$ArcSizeInfo.BSize",
27554                "ARCCache$BListVal.BListVal",
27555                "ARCCache$DefinedVal.DefinedVal",
27556            ] {
27557                assert!(
27558                    parsed
27559                        .declarations()
27560                        .iter()
27561                        .any(|unit| unit.fq_name() == fq_name),
27562                    "{dialect} reading must contain {fq_name}: {:#?}",
27563                    parsed.declarations()
27564                );
27565            }
27566            for flattened in [
27567                "ArcSizeInfo.BSize",
27568                "BListVal.BListVal",
27569                "DefinedVal.DefinedVal",
27570            ] {
27571                assert!(
27572                    parsed
27573                        .declarations()
27574                        .iter()
27575                        .all(|unit| unit.fq_name() != flattened),
27576                    "{dialect} reading must not flatten {flattened}: {:#?}",
27577                    parsed.declarations()
27578                );
27579            }
27580        }
27581    }
27582
27583    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
27584    #[test]
27585    #[hardwon(
27586        "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
27587    )]
27588    fn uppercase_c_extension_keeps_cpp_tag_scope() {
27589        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27590        let parsed = parse_cpp_declarations(source, "x.C");
27591        assert!(
27592            parsed
27593                .declarations()
27594                .iter()
27595                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
27596        );
27597    }
27598
27599    /// There is no such thing as a partially nested tag in C: every level of a
27600    /// nested aggregate chain lands at the same enclosing scope.
27601    #[test]
27602    #[hardwon(
27603        "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
27604    )]
27605    fn c_file_mints_every_nesting_level_at_file_scope() {
27606        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
27607        let parsed = parse_cpp_declarations(source, "z.c");
27608        let declarations = parsed.declarations();
27609
27610        for tag in ["a", "b", "c"] {
27611            assert!(
27612                declarations
27613                    .iter()
27614                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
27615                "expected a file-scope {tag}, got {declarations:?}"
27616            );
27617        }
27618        assert!(
27619            declarations
27620                .iter()
27621                .all(|unit| !unit.fq_name().contains('$')),
27622            "no level may keep a nested identity, got {declarations:?}"
27623        );
27624        // Each member still belongs to the aggregate that declares it.
27625        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
27626        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
27627        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
27628    }
27629
27630    /// An enum tag is a tag; its enumerators stay members of the enum, which is
27631    /// what makes them ordinary identifiers at the enum's own (file) scope.
27632    #[test]
27633    #[hardwon(
27634        "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
27635    )]
27636    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
27637        let source = "struct outer { enum color { RED, GREEN } c; };\n";
27638        let parsed = parse_cpp_declarations(source, "e.c");
27639        let declarations = parsed.declarations();
27640
27641        let color = declarations
27642            .iter()
27643            .find(|unit| unit.is_class() && unit.fq_name() == "color")
27644            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
27645        assert!(
27646            declarations
27647                .iter()
27648                .all(|unit| unit.fq_name() != "outer$color")
27649        );
27650        for enumerator in ["color.RED", "color.GREEN"] {
27651            assert!(
27652                declarations.iter().any(|unit| unit.fq_name() == enumerator),
27653                "expected {enumerator}, got {declarations:?}"
27654            );
27655        }
27656        let children = parsed
27657            .children
27658            .get(color)
27659            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
27660        assert!(
27661            ["color.RED", "color.GREEN"]
27662                .iter()
27663                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
27664            "enumerators must hang off their enum: {children:?}"
27665        );
27666    }
27667
27668    #[test]
27669    #[hardwon(
27670        "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
27671    )]
27672    fn c_file_mints_member_list_union_at_file_scope() {
27673        let source = "struct outer { union inner { int a; float b; } item; };\n";
27674        let parsed = parse_cpp_declarations(source, "u.c");
27675        let declarations = parsed.declarations();
27676        assert!(
27677            declarations
27678                .iter()
27679                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27680            "expected a file-scope inner union, got {declarations:?}"
27681        );
27682        assert!(
27683            declarations
27684                .iter()
27685                .all(|unit| unit.fq_name() != "outer$inner")
27686        );
27687        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
27688        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
27689    }
27690
27691    /// A tag declared in a namespace member list is not a file-scope tag: the
27692    /// nearest enclosing non-aggregate scope is the namespace.
27693    #[test]
27694    #[hardwon(
27695        "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
27696    )]
27697    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
27698        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
27699        let parsed = parse_cpp_declarations(source, "n.c");
27700        let declarations = parsed.declarations();
27701        let inner = declarations
27702            .iter()
27703            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
27704            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
27705        assert_eq!(inner.package_name(), "ns");
27706        assert!(
27707            declarations
27708                .iter()
27709                .all(|unit| unit.fq_name() != "ns.outer$inner")
27710        );
27711    }
27712
27713    /// A tag declared inside a function body: the C reading mints nothing for
27714    /// it (C block scope is a separate capability), while the C++ reading
27715    /// mints the local class under its block identity and its nested class
27716    /// under the local class (#6902). Neither reading flattens either tag into
27717    /// file scope.
27718    #[test]
27719    #[hardwon(
27720        "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
27721    )]
27722    fn function_local_tags_keep_block_scope_in_both_dialects() {
27723        let source =
27724            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
27725        for (name, local) in [("y.c", false), ("y.cpp", true)] {
27726            let parsed = parse_cpp_declarations(source, name);
27727            let declarations = parsed.declarations();
27728            assert!(
27729                declarations
27730                    .iter()
27731                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
27732                "{name}: {declarations:?}"
27733            );
27734            for tag in ["localtag", "deeper", "localtag$deeper"] {
27735                assert!(
27736                    declarations.iter().all(|unit| unit.fq_name() != tag),
27737                    "{name} must not mint {tag}, got {declarations:?}"
27738                );
27739            }
27740            for tag in [
27741                "run$block@1:16$localtag",
27742                "run$block@1:16$localtag$deeper",
27743                "run$block@1:16$localtag.d",
27744            ] {
27745                assert_eq!(
27746                    declarations.iter().any(|unit| unit.fq_name() == tag),
27747                    local,
27748                    "{name}: {tag} in {declarations:?}"
27749                );
27750            }
27751        }
27752    }
27753
27754    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
27755    /// re-scope: the typedef name is identical in both dialects.
27756    #[test]
27757    #[hardwon(
27758        "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
27759    )]
27760    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
27761        let source = "typedef struct { int v; } T;\n";
27762        for name in ["t.c", "t.cpp"] {
27763            let parsed = parse_cpp_declarations(source, name);
27764            let declarations = parsed.declarations();
27765            assert!(
27766                declarations
27767                    .iter()
27768                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
27769                "{name}: {declarations:?}"
27770            );
27771        }
27772    }
27773
27774    /// The same principle for every anonymous aggregate the walk indexes, not
27775    /// just the typedef one: a promoted anonymous union in a member list, an
27776    /// anonymous struct given a receiver type by its declarator, a
27777    /// function-local aggregate, and one inside an inline member function
27778    /// body. The C++ reading used to mint none of these, so a pure C++
27779    /// workspace indexed no members at all (#3301).
27780    #[test]
27781    #[hardwon(
27782        "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
27783    )]
27784    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
27785        let sources = [
27786            "struct outer { union { int a; float b; }; };\n",
27787            "struct outer { struct { int *ops; } sock; };\n",
27788            "void run(void) { struct { int v; } item; }\n",
27789            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
27790        ];
27791        for source in sources {
27792            let names = |name: &str| {
27793                parse_cpp_declarations(source, name)
27794                    .declarations()
27795                    .iter()
27796                    .map(|unit| unit.fq_name())
27797                    .collect::<std::collections::BTreeSet<_>>()
27798            };
27799            let c_names = names("a.c");
27800            let cpp_names = names("a.cpp");
27801            assert_eq!(
27802                c_names, cpp_names,
27803                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
27804            );
27805            assert!(
27806                c_names
27807                    .iter()
27808                    .any(|name| name.contains('.') && name != "outer"),
27809                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
27810            );
27811        }
27812    }
27813
27814    #[test]
27815    #[hardwon(
27816        "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
27817    )]
27818    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
27819        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
27820        let parsed = parse_cpp_declarations(source, "socket.c");
27821        let declarations = parsed.declarations();
27822        assert_eq!(
27823            declarations
27824                .iter()
27825                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
27826                .count(),
27827            1,
27828            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
27829        );
27830        for expected in [
27831            "PAL_HANDLE",
27832            "PAL_HANDLE.sock",
27833            "PAL_HANDLE$sock",
27834            "PAL_HANDLE$sock.ops",
27835        ] {
27836            assert!(
27837                declarations.iter().any(|unit| unit.fq_name() == expected),
27838                "expected {expected}, got {declarations:?}"
27839            );
27840        }
27841    }
27842
27843    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
27844    /// every (node, class-like name) pair a tree offers, and require the same
27845    /// answer from both.
27846    ///
27847    /// The release build has no `debug_assertions` agreement check, so this is
27848    /// what pins the two together there.  Both query orders are exercised:
27849    /// document order is what the walk does, and reverse order proves that a
27850    /// question about an earlier byte than one already answered is still
27851    /// filtered back to its own prefix rather than answered from the wider
27852    /// fold.
27853    ///
27854    /// Returns how many questions were answered with a namespace, so a fixture
27855    /// can assert it actually reached the path (#2754).
27856    fn namespace_forward_scan_agreement(source: &str) -> usize {
27857        let mut parser = tree_sitter::Parser::new();
27858        parser
27859            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27860            .unwrap();
27861        let tree = parser.parse(source, None).unwrap();
27862        let root = tree.root_node();
27863        let ancestry = ParentIndex::new(root);
27864
27865        let mut nodes = Vec::new();
27866        let mut names = std::collections::BTreeSet::new();
27867        let mut cursor = root.walk();
27868        let mut stack = vec![root];
27869        while let Some(node) = stack.pop() {
27870            if matches!(
27871                node.kind(),
27872                "class_specifier" | "struct_specifier" | "union_specifier"
27873            ) && let Some(name) = class_like_name(node, source, &ancestry)
27874            {
27875                names.insert(name);
27876            }
27877            nodes.push(node);
27878            stack.extend(node.named_children(&mut cursor));
27879        }
27880        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
27881        assert!(!names.is_empty(), "fixture declares no class-like name");
27882
27883        let mut answered = 0usize;
27884        for reversed in [false, true] {
27885            let mut scan = CppNamespaceForwardScan::default();
27886            let ordered: Vec<_> = if reversed {
27887                nodes.iter().rev().copied().collect()
27888            } else {
27889                nodes.clone()
27890            };
27891            answered = 0;
27892            for node in ordered {
27893                for name in &names {
27894                    scan.advance_to(root, node.start_byte(), source, &ancestry);
27895                    let carried = scan.unique_earlier_forward(name, node);
27896                    assert_eq!(
27897                        carried,
27898                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
27899                        "carried-forward scan and prefix scan disagree about {name} at \
27900                         {} node starting at byte {} (reversed order: {reversed})",
27901                        node.kind(),
27902                        node.start_byte()
27903                    );
27904                    answered += usize::from(carried.is_some());
27905                }
27906            }
27907        }
27908        answered
27909    }
27910
27911    /// A malformed namespace whose forward declarations are the only identity
27912    /// signal left for the class definitions tree-sitter pushed out to file
27913    /// scope.  Both recovered classes open the guard; only the first one is
27914    /// separated from the namespace by nothing but recovery trivia, so only the
27915    /// first one borrows.  The carried-forward scan has to reproduce both
27916    /// answers.
27917    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
27918namespace ns {
27919class Widget;
27920class Gadget;
27921int x = ;
27922}
27923class API Widget {
27924public:
27925    void first();
27926};
27927class API Gadget {
27928public:
27929    void second();
27930};
27931"#;
27932
27933    #[test]
27934    #[hardwon(
27935        "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
27936    )]
27937    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
27938        assert!(
27939            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
27940            "the fixture must actually reach the namespace-borrow path"
27941        );
27942
27943        // Nothing here may be answered, and the two paths have to agree about
27944        // that too: a clean namespace is not an identity proof, two forwards of
27945        // one name are ambiguous rather than a guess, and a forward inside a
27946        // function body is not at namespace scope.
27947        for source in [
27948            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
27949            r#"#define API
27950namespace ns {
27951class Widget;
27952class Widget;
27953int x = ;
27954}
27955class API Widget {
27956public:
27957    void method();
27958};
27959"#,
27960            r#"#define API
27961namespace ns {
27962void host() {
27963    class Widget;
27964}
27965int x = ;
27966}
27967class API Widget {
27968public:
27969    void method();
27970};
27971"#,
27972        ] {
27973            assert_eq!(
27974                namespace_forward_scan_agreement(source),
27975                0,
27976                "no borrow is justified here: {source}"
27977            );
27978        }
27979    }
27980
27981    /// Drive the region reparse and the whitespace-padded reparse it replaced
27982    /// over the same region and require identical trees.
27983    ///
27984    /// The release build has no `debug_assertions` agreement check, so this is
27985    /// what pins the two together there. Each body is reparsed at its own
27986    /// offset and again after a long prefix, because the prefix is the whole
27987    /// difference between the two techniques: the padded parse lexes it as
27988    /// whitespace, the included-range parse never sees it, and the tree has to
27989    /// come out the same either way (#2788).
27990    fn fragmented_class_reparse_agreement(body: &str) {
27991        for prefix in [
27992            String::new(),
27993            "// leading comment\n".to_string(),
27994            // A body starts just after its class head's `{`, which is normally
27995            // mid-line: the padded parse then has spaces before the region on
27996            // the region's own line, and the included-range parse has nothing
27997            // at all before it.
27998            "class Widget : public Base { ".to_string(),
27999            "namespace filler {\n".to_string()
28000                + &"struct Filler { int member; };\n".repeat(200)
28001                + "}\n",
28002            "namespace filler {\n".to_string()
28003                + &"struct Filler { int member; };\n".repeat(200)
28004                + "}\nclass Widget : public Base { ",
28005        ] {
28006            let source = format!("{prefix}{body}");
28007            let start = prefix.len();
28008            let end = source.len();
28009            let region = cpp_reparse_fragmented_class_body(&source, start, end)
28010                .expect("the region reparse must produce a tree");
28011            let padded = cpp_reparse_padded_class_body(&source, start, end)
28012                .expect("the padded reparse must produce a tree");
28013            assert_eq!(
28014                cpp_tree_shape(&region),
28015                cpp_tree_shape(&padded),
28016                "region and padded reparse disagree at offset {start} of {end} bytes"
28017            );
28018            assert_eq!(
28019                region.root_node().start_byte(),
28020                start,
28021                "the reparsed region keeps its original offsets"
28022            );
28023        }
28024    }
28025
28026    #[test]
28027    #[hardwon(
28028        "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
28029    )]
28030    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
28031        // A conditional immediately after an access label: the shape the padded
28032        // technique was kept for, because the directive and its macro name
28033        // become an ERROR plus the following declaration's apparent type.
28034        fragmented_class_reparse_agreement(
28035            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
28036        );
28037        fragmented_class_reparse_agreement(
28038            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
28039        );
28040        // The merged inline constructor and the nested fragmented bodies the
28041        // #938 recovery reads out of a reparse.
28042        fragmented_class_reparse_agreement(
28043            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
28044        );
28045        fragmented_class_reparse_agreement(
28046            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
28047        );
28048        // A body that is not member-shaped at all still has to reparse the same
28049        // way, because the admission gate reads the tree to reject it.
28050        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
28051    }
28052
28053    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
28054        let mut siblings = Vec::new();
28055        let mut anchor = node;
28056        while let Some(parent) = anchor.parent() {
28057            let at_translation_unit = parent.kind() == "translation_unit";
28058            let mut sibling = anchor.next_named_sibling();
28059            while let Some(current) = sibling {
28060                if at_translation_unit
28061                    && (current.kind() == "namespace_definition"
28062                        || (current.kind() == "function_definition"
28063                            && first_class_like_child(current).is_some()))
28064                {
28065                    return siblings;
28066                }
28067                siblings.push(current);
28068                if cpp_is_stray_close_brace(current, source) {
28069                    if let Some(semicolon) = current
28070                        .next_named_sibling()
28071                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
28072                    {
28073                        siblings.push(semicolon);
28074                    }
28075                    return siblings;
28076                }
28077                if current.start_byte() >= node.end_byte()
28078                    && matches!(current.kind(), "ERROR" | "labeled_statement")
28079                    && cpp_nested_stray_close_brace(current, source).is_some()
28080                {
28081                    return siblings;
28082                }
28083                sibling = current.next_named_sibling();
28084            }
28085            anchor = parent;
28086        }
28087        siblings
28088    }
28089
28090    #[test]
28091    #[hardwon(
28092        "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
28093    )]
28094    fn following_named_siblings_cursor_matches_native_traversal() {
28095        // A damaged export-class head scatters members across container
28096        // siblings, and the anonymous punctuation forces the cursor to filter
28097        // children exactly as Node::next_named_sibling does. The grammar reads
28098        // a function-like export macro head since brokk-tree-sitter-cpp
28099        // 0.24.7, so the fixture uses an object-like macro before a base
28100        // clause, which still has no parse.
28101        let source = r#"
28102namespace api {
28103class PROJECT_API Widget : public virtual Base {
28104  public:
28105    Widget() {}
28106    void first();
28107};
28108enum After { Value };
28109}
28110"#;
28111        let mut parser = tree_sitter::Parser::new();
28112        parser
28113            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28114            .unwrap();
28115        let tree = parser.parse(source, None).unwrap();
28116        let root = tree.root_node();
28117        assert!(root.has_error(), "fixture must remain damaged");
28118
28119        let ancestry = ParentIndex::new(root);
28120        let mut stack = vec![root];
28121        let mut checked = 0;
28122        let mut damaged = 0;
28123        let mut anonymous = 0;
28124        let mut nonempty_sequences = 0;
28125        let mut named_then_anonymous = 0;
28126        while let Some(node) = stack.pop() {
28127            let expected = native_following_named_siblings(node, source)
28128                .iter()
28129                .map(Node::id)
28130                .collect::<Vec<_>>();
28131            let actual = cpp_following_named_siblings(node, source, &ancestry)
28132                .iter()
28133                .map(Node::id)
28134                .collect::<Vec<_>>();
28135            assert_eq!(
28136                actual,
28137                expected,
28138                "cursor traversal diverged at byte {}",
28139                node.start_byte()
28140            );
28141
28142            let mut cursor = node.walk();
28143            let children = node.children(&mut cursor).collect::<Vec<_>>();
28144            stack.extend(children.iter().copied());
28145            checked += 1;
28146            damaged += usize::from(node.is_error() || node.is_missing());
28147            anonymous += usize::from(!node.is_named());
28148            nonempty_sequences += usize::from(!actual.is_empty());
28149            named_then_anonymous += usize::from(
28150                children
28151                    .windows(2)
28152                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
28153            );
28154        }
28155        assert!(checked > 0, "fixture must provide nodes to compare");
28156        assert!(damaged > 0, "fixture must cover damaged nodes");
28157        assert!(anonymous > 0, "fixture must cover anonymous nodes");
28158        assert!(
28159            nonempty_sequences > 0,
28160            "fixture must exercise following named siblings"
28161        );
28162        assert!(
28163            named_then_anonymous > 0,
28164            "fixture must exercise anonymous nodes following a named anchor"
28165        );
28166    }
28167
28168    #[test]
28169    #[hardwon(
28170        "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
28171    )]
28172    fn a_replacement_removes_owned_enum_fields() {
28173        let source =
28174            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
28175        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
28176        let mut names: Vec<_> = parsed
28177            .declarations()
28178            .iter()
28179            .map(|unit| unit.fq_name())
28180            .collect();
28181        names.sort();
28182        assert_eq!(
28183            names,
28184            vec![
28185                "Color".to_string(),
28186                "First".to_string(),
28187                "First.A".to_string()
28188            ],
28189            "the replaced Color owns no field any more"
28190        );
28191    }
28192
28193    /// A recovery that re-declares what the file already declared mints
28194    /// nothing.
28195    ///
28196    /// The reparse walk replaces the outer `Widget`, which removes its method,
28197    /// and then re-creates that method from the region. Creation alone would
28198    /// call the method recovered; it was there before the recovery opened, so
28199    /// the recovered set is empty and only the region's own reparse window is
28200    /// recorded (#2787).
28201    #[test]
28202    #[hardwon(
28203        "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
28204    )]
28205    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
28206        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
28207                      BEGIN_NS\n\
28208                      namespace demo { struct Widget { void doWork(); }; }\n\
28209                      END_NS\n";
28210        let parsed = parse_cpp_declarations(source, "restored.cpp");
28211        let recovered: Vec<String> = parsed
28212            .materialization_records
28213            .iter()
28214            .filter_map(|record| match record {
28215                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
28216                _ => None,
28217            })
28218            .collect();
28219        assert!(
28220            recovered.is_empty(),
28221            "the region declares nothing the file did not already declare: {recovered:?}"
28222        );
28223        let mut names: Vec<String> = parsed
28224            .declarations()
28225            .iter()
28226            .map(|unit| unit.fq_name())
28227            .collect();
28228        names.sort();
28229        assert_eq!(
28230            names,
28231            vec![
28232                "demo".to_string(),
28233                "demo.Widget".to_string(),
28234                "demo.Widget.doWork".to_string(),
28235            ]
28236        );
28237    }
28238
28239    /// Four macro-sentinel recoveries in one file (#941). Each one must record
28240    /// exactly the declarations it minted -- not the ones an earlier recovery
28241    /// minted, and not the file's other declarations -- in start-byte order,
28242    /// and each record must carry its own reparse window (#2787).
28243    #[test]
28244    #[hardwon(
28245        "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28246    )]
28247    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28248        let mut source = String::new();
28249        for index in 0..4 {
28250            let _ = write!(
28251                source,
28252                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
28253            );
28254        }
28255        source.push_str("void outside() {}\n");
28256        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28257
28258        let recovered: Vec<(String, (usize, usize))> = parsed
28259            .materialization_records
28260            .iter()
28261            .filter_map(|record| match record {
28262                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28263                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28264                }
28265                _ => None,
28266            })
28267            .collect();
28268
28269        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28270        for index in 0..4 {
28271            // The window the reparse covers: everything after the opening
28272            // sentinel token up to the newline before the closing one.
28273            let region = format!("namespace demo{index}");
28274            let region_start = source.find(&region).expect("each region is in the source");
28275            let start = source[..region_start]
28276                .rfind("BEGIN_NS")
28277                .expect("each region opens with a sentinel")
28278                + "BEGIN_NS".len();
28279            let end = start
28280                + source[start..]
28281                    .find("END_NS")
28282                    .expect("each region closes with a sentinel")
28283                - 1;
28284            let window = (start, end);
28285            for name in [
28286                format!("demo{index}"),
28287                format!("demo{index}.Widget{index}"),
28288                format!("demo{index}.Widget{index}.doWork{index}"),
28289            ] {
28290                expected.push((name, window));
28291            }
28292        }
28293        assert_eq!(
28294            recovered, expected,
28295            "each recovery records its own minted declarations, in order"
28296        );
28297        assert!(
28298            parsed
28299                .declarations()
28300                .iter()
28301                .any(|unit| unit.fq_name() == "outside"),
28302            "the declaration outside every region stays parsed and unrecovered"
28303        );
28304    }
28305
28306    /// `blocks` copies of one plain class-with-methods template. The class the
28307    /// question is about is always the first one, so the only thing that
28308    /// changes between two of these sources is how much unrelated tree
28309    /// surrounds it.
28310    fn repeated_class_blocks(blocks: usize) -> String {
28311        let mut source = String::from("namespace demo {\n");
28312        for index in 0..blocks {
28313            let _ = write!(
28314                source,
28315                "\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"
28316            );
28317        }
28318        source.push_str("\n}\n");
28319        source
28320    }
28321
28322    /// #1496: asking whether a recovered class shape claims one range must cost
28323    /// the path to that range, not a pass over the whole translation unit.
28324    ///
28325    /// The C++ inverse scan asks this once per candidate type reference, so a
28326    /// whole-tree walk makes one file's scan quadratic in its own size. Both
28327    /// sources here answer `None` -- these are plain classes that no recovery
28328    /// shape claims -- which is exactly the case that used to pay full price.
28329    #[test]
28330    #[hardwon(
28331        "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28332    )]
28333    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28334        let mut answers = Vec::new();
28335        let mut visits = Vec::new();
28336        let mut node_counts = Vec::new();
28337        for blocks in [200usize, 400] {
28338            let source = repeated_class_blocks(blocks);
28339            let mut parser = tree_sitter::Parser::new();
28340            parser
28341                .set_language(&tree_sitter_cpp::LANGUAGE.into())
28342                .unwrap();
28343            let tree = parser.parse(&source, None).unwrap();
28344            let start_byte = source.find("class Widget0 ").expect("first class");
28345            let end_byte = start_byte
28346                + source[start_byte..]
28347                    .find("};")
28348                    .expect("first class terminator")
28349                + "};".len();
28350            let range = Range {
28351                start_byte,
28352                end_byte,
28353                start_line: 0,
28354                end_line: 0,
28355            };
28356            reset_recovered_class_body_node_visits_for_test();
28357            let recovered_export_classes =
28358                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28359            answers.push(recovered_class_body_at(
28360                &recovered_export_classes,
28361                tree.root_node(),
28362                &source,
28363                "Widget0",
28364                &range,
28365            ));
28366            visits.push(recovered_class_body_node_visits_for_test());
28367            let mut nodes = 0usize;
28368            let mut stack = vec![tree.root_node()];
28369            while let Some(node) = stack.pop() {
28370                nodes += 1;
28371                let mut cursor = node.walk();
28372                stack.extend(node.named_children(&mut cursor));
28373            }
28374            node_counts.push(nodes);
28375        }
28376
28377        assert_eq!(
28378            answers,
28379            vec![None, None],
28380            "no recovered shape claims a plain class"
28381        );
28382        assert_eq!(
28383            visits[0], visits[1],
28384            "the walk must follow the range's own path, so doubling the unrelated \
28385             classes must not change the node count: {visits:?} over trees of \
28386             {node_counts:?} nodes"
28387        );
28388        assert!(
28389            visits[1] * 20 < node_counts[1],
28390            "the walk must stay far below one pass over the tree: {visits:?} over \
28391             trees of {node_counts:?} nodes"
28392        );
28393    }
28394
28395    #[test]
28396    #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
28397    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
28398        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
28399        let mut parser = tree_sitter::Parser::new();
28400        parser
28401            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28402            .expect("C++ grammar");
28403        let tree = parser.parse(source, None).expect("fixture tree");
28404        let mut stack = vec![tree.root_node()];
28405        let mut recovered = None;
28406        while let Some(node) = stack.pop() {
28407            if node.kind() == "field_declaration"
28408                && let Some(field) = recovered_function_like_field_declarator(node, source)
28409            {
28410                recovered = Some((node, field.name));
28411                break;
28412            }
28413            let mut cursor = node.walk();
28414            stack.extend(node.named_children(&mut cursor));
28415        }
28416        let (declaration, name) = recovered.unwrap_or_else(|| {
28417            panic!(
28418                "pointer-wrapped macro field was not recovered: {}",
28419                tree.root_node().to_sexp()
28420            )
28421        });
28422        assert_eq!(node_text(name, source), "handshake");
28423        let recovered =
28424            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
28425        assert_eq!(recovered.pointer_depth(), 1);
28426        assert_eq!(
28427            render_cpp_field_signature(declaration, name, source),
28428            "struct handshake * handshake;"
28429        );
28430    }
28431
28432    #[test]
28433    #[hardwon(
28434        "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
28435    )]
28436    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
28437        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
28438        let mut parser = tree_sitter::Parser::new();
28439        parser
28440            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28441            .expect("C++ grammar");
28442        let tree = parser.parse(source, None).expect("fixture tree");
28443        // The macro-declaration grammar reads `PyObject_HEAD` as a
28444        // `macro_modifier`, so the member arrives as an ordinary clean field
28445        // declaration rather than the recovery shape the ERROR-based reader
28446        // exists for.
28447        let mut stack = vec![tree.root_node()];
28448        let mut declaration = None;
28449        while let Some(node) = stack.pop() {
28450            if node.kind() == "field_declaration" {
28451                declaration = Some(node);
28452                break;
28453            }
28454            let mut cursor = node.walk();
28455            stack.extend(node.named_children(&mut cursor));
28456        }
28457        let declaration = declaration.unwrap_or_else(|| {
28458            panic!(
28459                "the pointer field must arrive as an ordinary field declaration: {}",
28460                tree.root_node().to_sexp()
28461            )
28462        });
28463        let declarator = declaration
28464            .child_by_field_name("declarator")
28465            .expect("pointer field declarator");
28466        assert_eq!(node_text(declarator, source), "*image");
28467        assert_eq!(
28468            render_cpp_field_signature(declaration, declarator, source),
28469            "PyObject_HEAD ImagingObject * image;",
28470            "the macro marker is retained as modifier provenance, the same \
28471             treatment an attribute or storage-class specifier gets"
28472        );
28473        assert_eq!(
28474            crate::source_facts::capture_field_type(declaration, "image", source),
28475            (
28476                Some(
28477                    brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
28478                        type_text: "ImagingObject".to_owned(),
28479                        indirection: 1,
28480                        binds_indirectly: true,
28481                        template_arguments: None,
28482                    }
28483                ),
28484                false,
28485            ),
28486            "canonical field facts must use the declared type, not the macro token"
28487        );
28488    }
28489
28490    #[test]
28491    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
28492        let source = "class outer { class inner { int v; }; };\n";
28493        let c_parsed = parse_cpp_declarations(source, "k.c");
28494        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
28495        let c_declarations = c_parsed.declarations();
28496        let cpp_declarations = cpp_parsed.declarations();
28497        assert!(
28498            c_declarations
28499                .iter()
28500                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28501            "{c_declarations:?}"
28502        );
28503        assert_eq!(
28504            c_declarations
28505                .iter()
28506                .map(|unit| unit.fq_name())
28507                .collect::<std::collections::BTreeSet<_>>(),
28508            cpp_declarations
28509                .iter()
28510                .map(|unit| unit.fq_name())
28511                .collect::<std::collections::BTreeSet<_>>()
28512        );
28513    }
28514    #[test]
28515    fn callable_metadata_records_c_receiver_contracts_structurally() {
28516        const SOURCE: &str = concat!(
28517            "static long accumulate(const long *lines, int count) { return count; }\n",
28518            "\n",
28519            "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
28520        );
28521        let parsed = parse_cpp_declarations(SOURCE, "shop.c");
28522
28523        assert_eq!(
28524            recorded_callable_modifiers(&parsed),
28525            vec![
28526                "accumulate decl_only=false static=false constructor=false \
28527                 visibility=Unknown"
28528                    .to_string(),
28529                "total decl_only=false static=false constructor=false visibility=Unknown"
28530                    .to_string(),
28531            ],
28532        );
28533    }
28534    #[test]
28535    fn callable_metadata_records_cpp_receiver_contracts_structurally() {
28536        const SOURCE: &str = concat!(
28537            "namespace shop {\n",
28538            "\n",
28539            "class Client {\n",
28540            "public:\n",
28541            "    Client();\n",
28542            "    void send(int order);\n",
28543            "    static int count();\n",
28544            "protected:\n",
28545            "    virtual int audit(int n) const { return n; }\n",
28546            "private:\n",
28547            "    int tally(int n) { return n; }\n",
28548            "};\n",
28549            "\n",
28550            "struct Ledger {\n",
28551            "    void record(int n) { (void)n; }\n",
28552            "};\n",
28553            "\n",
28554            "Client::Client() {}\n",
28555            "void Client::send(int order) { (void)order; }\n",
28556            "int Client::count() { return 0; }\n",
28557            "\n",
28558            "int total(int order) { return order; }\n",
28559            "\n",
28560            "}\n",
28561        );
28562        let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
28563
28564        assert_eq!(
28565            recorded_callable_modifiers(&parsed),
28566            vec![
28567                "shop.Client.Client decl_only=false static=false constructor=true \
28568                 visibility=Unknown"
28569                    .to_string(),
28570                "shop.Client.Client decl_only=true static=false constructor=true \
28571                 visibility=Public"
28572                    .to_string(),
28573                "shop.Client.audit decl_only=false static=false constructor=false \
28574                 visibility=Protected"
28575                    .to_string(),
28576                "shop.Client.count decl_only=false static=false constructor=false \
28577                 visibility=Unknown"
28578                    .to_string(),
28579                "shop.Client.count decl_only=true static=true constructor=false \
28580                 visibility=Public"
28581                    .to_string(),
28582                "shop.Client.send decl_only=false static=false constructor=false \
28583                 visibility=Unknown"
28584                    .to_string(),
28585                "shop.Client.send decl_only=true static=false constructor=false \
28586                 visibility=Public"
28587                    .to_string(),
28588                "shop.Client.tally decl_only=false static=false constructor=false \
28589                 visibility=Private"
28590                    .to_string(),
28591                "shop.Ledger.record decl_only=false static=false constructor=false \
28592                 visibility=Public"
28593                    .to_string(),
28594                "shop.total decl_only=false static=false constructor=false visibility=Unknown"
28595                    .to_string(),
28596            ],
28597        );
28598    }
28599
28600    #[test]
28601    fn reparsed_member_signature_grammar_contract() {
28602        use crate::grammar_test_support::{nodes, tree};
28603
28604        // These are class-member fragments reparsed at translation-unit scope,
28605        // exactly the boundary the recovery adapter receives from the grammar.
28606        // Identifier-only annotation errors retain a complete declaration;
28607        // literal payloads and missing signature tokens cannot be admitted.
28608        for (source, expected, error_children, missing_close) in [
28609            ("int f(int n) const { return n; }", true, vec![], false),
28610            (
28611                "int f(int n) const nonneg { return n; }",
28612                true,
28613                vec!["identifier"],
28614                false,
28615            ),
28616            (
28617                "int f(int n) ATTR 42 { return n; }",
28618                false,
28619                vec!["identifier", "number_literal"],
28620                false,
28621            ),
28622            (
28623                "int f(int n = 42 43) { return n; }",
28624                false,
28625                vec!["number_literal"],
28626                false,
28627            ),
28628            ("int f(int n { return n; }", false, vec![], true),
28629        ] {
28630            let parsed = tree(source);
28631            let root = parsed.root_node();
28632            let function = root.named_child(0).unwrap();
28633            assert_eq!(
28634                function.kind(),
28635                "function_definition",
28636                "{source}: {}",
28637                root.to_sexp()
28638            );
28639            let declarator = function.child_by_field_name("declarator").unwrap();
28640            assert_eq!(declarator.kind(), "function_declarator");
28641            let parameters = declarator.child_by_field_name("parameters").unwrap();
28642            assert_eq!(parameters.kind(), "parameter_list");
28643            assert_eq!(
28644                function.child_by_field_name("body").unwrap().kind(),
28645                "compound_statement"
28646            );
28647            let all_nodes = nodes(function);
28648            let errors: Vec<_> = all_nodes
28649                .iter()
28650                .filter(|node| node.kind() == "ERROR")
28651                .collect();
28652            let kinds: Vec<_> = errors
28653                .iter()
28654                .flat_map(|error| {
28655                    error
28656                        .named_children(&mut error.walk())
28657                        .map(|child| child.kind())
28658                        .collect::<Vec<_>>()
28659                })
28660                .collect();
28661            assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
28662            assert_eq!(
28663                all_nodes
28664                    .iter()
28665                    .any(|node| node.is_missing() && node.kind() == ")"),
28666                missing_close,
28667                "{source}"
28668            );
28669            assert_eq!(
28670                cpp_reparsed_members_are_indexable(root, source),
28671                expected,
28672                "{source}: {}",
28673                root.to_sexp()
28674            );
28675        }
28676    }
28677    #[test]
28678    fn qualified_and_template_declarator_name_grammar_contract() {
28679        use crate::grammar_test_support::{nodes, tree};
28680        let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
28681        let parsed = tree(source);
28682        let all = nodes(parsed.root_node());
28683        for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
28684            let qualified = all
28685                .iter()
28686                .copied()
28687                .find(|node| {
28688                    node.kind() == "qualified_identifier" && node_text(*node, source) == written
28689                })
28690                .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
28691            let scope = qualified.child_by_field_name("scope").unwrap();
28692            assert_eq!(node_text(scope, source), owner);
28693            assert!(qualified.child_by_field_name("name").is_some());
28694            assert_eq!(extract_declarator_name(qualified, source), written);
28695        }
28696        let function = all
28697            .iter()
28698            .copied()
28699            .find(|node| {
28700                node.kind() == "function_declarator"
28701                    && node
28702                        .child_by_field_name("declarator")
28703                        .is_some_and(|name| name.kind() == "template_function")
28704            })
28705            .unwrap_or_else(|| {
28706                panic!(
28707                    "missing template declarator: {}",
28708                    parsed.root_node().to_sexp()
28709                )
28710            });
28711        let template = function.child_by_field_name("declarator").unwrap();
28712        let name = template.child_by_field_name("name").unwrap();
28713        assert_eq!(name.kind(), "identifier");
28714        assert_eq!(node_text(name, source), "make");
28715        assert_eq!(
28716            template.child_by_field_name("arguments").unwrap().kind(),
28717            "template_argument_list"
28718        );
28719        assert_eq!(cpp_function_declarator_name_node(function), Some(name));
28720    }
28721
28722    fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
28723        let mut declarations = file
28724            .declarations()
28725            .iter()
28726            .map(|unit| {
28727                let callable = unit.is_function().then(|| {
28728                    let mut labels = file
28729                        .signature_metadata
28730                        .get(unit)
28731                        .into_iter()
28732                        .flatten()
28733                        .map(|metadata| {
28734                            metadata
28735                                .parameters()
28736                                .iter()
28737                                .map(|parameter| parameter.label())
28738                                .collect::<Vec<_>>()
28739                        })
28740                        .collect::<Vec<_>>();
28741                    labels.sort();
28742                    (unit.signature(), labels)
28743                });
28744                let fields = unit.is_field().then(|| {
28745                    let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
28746                    signatures.sort();
28747                    signatures.dedup();
28748                    signatures
28749                });
28750                format!(
28751                    "{:?} {} {fields:?} {callable:?}",
28752                    unit.kind(),
28753                    unit.fq_name()
28754                )
28755            })
28756            .collect::<Vec<_>>();
28757        declarations.sort();
28758        declarations
28759    }
28760
28761    /// Metamorphic oracle: erasing declaration decorators, expanding field-list
28762    /// macros, and spelling a namespace explicitly preserve declaration identity.
28763    /// Compare the complete inventory so a recovered initializer or macro token
28764    /// cannot pass merely because the intended callable was also recovered.
28765    #[test]
28766    fn recovery_declarations_agree_with_explicit_expansions() {
28767        let cases = [
28768            (
28769                "decorated constructors",
28770                "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",
28771                "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",
28772            ),
28773            (
28774                "namespace sentinel",
28775                "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
28776                "namespace demo { class Widget { public: int first; void method(); }; }\n",
28777            ),
28778            (
28779                "wrapped prototypes",
28780                "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",
28781                "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
28782            ),
28783            (
28784                "namespace forward across directives",
28785                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
28786                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
28787            ),
28788            (
28789                "local forward does not lend namespace",
28790                "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
28791                "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
28792            ),
28793            (
28794                "object declarator does not lend namespace",
28795                "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
28796                "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
28797            ),
28798            (
28799                "const template return",
28800                r###"template<class T> struct C { API const T f() { return {}; } };"###,
28801                r###"template<class T> struct C { const T f() { return {}; } };"###,
28802            ),
28803            (
28804                "collapsed struct and union",
28805                r###"#define FIRST int first;
28806#define SECOND int first; int second;
28807union One { FIRST SECOND
28808};
28809struct Two { FIRST SECOND
28810};
28811struct Tail { UNDEFINED
28812};
28813"###,
28814                r###"#define FIRST int first;
28815#define SECOND int first; int second;
28816union One { int first; int first; int second;
28817};
28818struct Two { int first; int first; int second;
28819};
28820struct Tail { UNDEFINED
28821};
28822"###,
28823            ),
28824            (
28825                "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
28826                r###"
28827#pragma once
28828#define DEMO_DEPRECATED(message)
28829namespace demo {
28830struct Base {
28831    static int aligned(int value) { return value; }
28832    int legacy(int value) const
28833        DEMO_DEPRECATED("use replacement()") { return value; }
28834    int replacement() const;
28835    void run(int value);
28836};
28837struct OtherBase {
28838    void run(int value);
28839    static int aligned(int value) { return value; }
28840};
28841struct Derived : Base {};
28842struct Override : Base {
28843    void run(int value);
28844    static int aligned(int value) { return value; }
28845};
28846struct RecoveredOverride : Base {
28847    int legacy(int value) const
28848        DEMO_DEPRECATED("use replacement()") { return value; }
28849    void run(int value);
28850};
28851struct Hidden : Base {
28852    void run(int first, int second);
28853    static int aligned(int first, int second) { return first + second; }
28854};
28855struct Ambiguous : Base, OtherBase {};
28856}
28857struct Global {};
28858"###,
28859                r###"
28860#pragma once
28861#define DEMO_DEPRECATED(message)
28862namespace demo {
28863struct Base {
28864    static int aligned(int value) { return value; }
28865    int legacy(int value) const
28866         { return value; }
28867    int replacement() const;
28868    void run(int value);
28869};
28870struct OtherBase {
28871    void run(int value);
28872    static int aligned(int value) { return value; }
28873};
28874struct Derived : Base {};
28875struct Override : Base {
28876    void run(int value);
28877    static int aligned(int value) { return value; }
28878};
28879struct RecoveredOverride : Base {
28880    int legacy(int value) const
28881         { return value; }
28882    void run(int value);
28883};
28884struct Hidden : Base {
28885    void run(int first, int second);
28886    static int aligned(int first, int second) { return first + second; }
28887};
28888struct Ambiguous : Base, OtherBase {};
28889}
28890struct Global {};
28891"###,
28892            ),
28893            (
28894                "sentinel union",
28895                r###"BEGIN_NS
28896union Storage {int value; char byte;};
28897END_NS
28898"###,
28899                r###"union Storage {int value; char byte;};
28900"###,
28901            ),
28902            (
28903                "compact sentinel class",
28904                r###"BEGIN_NS
28905class Widget {int first; void method();};
28906END_NS
28907"###,
28908                r###"class Widget {int first; void method();};
28909"###,
28910            ),
28911            (
28912                "three decorators",
28913                "class C { FIRST SECOND THIRD void f() {} };",
28914                "class C { void f() {} };",
28915            ),
28916            (
28917                "overlapping field lists",
28918                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
28919                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
28920            ),
28921            (
28922                "identical redefinition",
28923                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
28924                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
28925            ),
28926            (
28927                "unconditional reset",
28928                "#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",
28929                "#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",
28930            ),
28931        ];
28932        for (label, source_lf, expanded_lf) in cases {
28933            for (source, expanded) in [
28934                (source_lf.to_owned(), expanded_lf.to_owned()),
28935                (
28936                    source_lf.replace('\n', "\r\n"),
28937                    expanded_lf.replace('\n', "\r\n"),
28938                ),
28939            ] {
28940                let tree = crate::grammar_test_support::tree(&source);
28941                let nodes = crate::grammar_test_support::nodes(tree.root_node());
28942                match label {
28943                    "decorated constructors" => assert!(
28944                        nodes.iter().any(|node| {
28945                            node.kind() == "ERROR"
28946                                && node
28947                                    .named_child(0)
28948                                    .is_some_and(|head| head.kind() == "macro_modifier_declarator")
28949                        }),
28950                        "{}",
28951                        tree.root_node().to_sexp()
28952                    ),
28953                    "wrapped prototypes" => assert!(
28954                        tree.root_node().named_child(0).is_some_and(|node| {
28955                            node.kind() == "ERROR"
28956                                && node
28957                                    .named_child(0)
28958                                    .is_some_and(|head| head.kind() == "function_declarator")
28959                        }),
28960                        "{}",
28961                        tree.root_node().to_sexp()
28962                    ),
28963                    "collapsed struct and union" => assert!(
28964                        nodes.iter().any(|node| {
28965                            node.kind() == "union"
28966                                && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
28967                        }),
28968                        "{}",
28969                        tree.root_node().to_sexp()
28970                    ),
28971                    "object declarator does not lend namespace" => assert!(
28972                        nodes
28973                            .iter()
28974                            .any(|node| { node.kind() == "parenthesized_declarator" }),
28975                        "{}",
28976                        tree.root_node().to_sexp()
28977                    ),
28978                    _ => {}
28979                }
28980                let parsed = parse_cpp_declarations(&source, "expansion.h");
28981                let explicit = parse_cpp_declarations(&expanded, "expansion.h");
28982                assert_eq!(
28983                    declaration_expansion_inventory(&parsed),
28984                    declaration_expansion_inventory(&explicit),
28985                    "{label}"
28986                );
28987                for unit in parsed.declarations() {
28988                    for range in parsed.declaration_ranges(unit) {
28989                        assert!(
28990                            range.start_byte < range.end_byte,
28991                            "{label}: {unit:?}: {range:?}"
28992                        );
28993                        for (offset, line) in [
28994                            (range.start_byte, range.start_line),
28995                            (range.end_byte, range.end_line),
28996                        ] {
28997                            assert_eq!(
28998                                line,
28999                                source[..offset]
29000                                    .bytes()
29001                                    .filter(|byte| *byte == b'\n')
29002                                    .count()
29003                                    + 1,
29004                                "{label}: {unit:?}: {range:?}"
29005                            );
29006                        }
29007                    }
29008                }
29009            }
29010        }
29011    }
29012
29013    #[test]
29014    #[hardwon(
29015        "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
29016    )]
29017    fn recovered_class_constructor_agrees_with_explicit_expansion() {
29018        let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
29019        let tree = crate::grammar_test_support::tree(source);
29020        crate::grammar_test_support::assert_function_like_export_class_head(&tree, source, "value");
29021        let actual = parse_cpp_declarations(source, "constructor.h");
29022        let explicit = parse_cpp_declarations(
29023            "class value { public: value(pn_data_t* d); int following(); };",
29024            "constructor.h",
29025        );
29026        assert_eq!(function_identities(&actual), function_identities(&explicit));
29027    }
29028
29029    #[test]
29030    #[hardwon(
29031        "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
29032    )]
29033    fn recovered_base_initializer_is_not_a_declaration() {
29034        let source = r###"
29035namespace api {
29036
29037/**
29038* Doc comment
29039*/
29040class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
29041   public:
29042      /**
29043      * Construct from a point.
29044      */
29045      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
29046
29047#if defined(PROJECT_HAS_LEGACY_POINT)
29048      /**
29049      * Construct from a legacy point.
29050      */
29051      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
29052#endif
29053
29054      std::string algo_name() const override;
29055
29056      AlgorithmIdentifier algorithm_identifier() const override;
29057};
29058
29059}
29060"###;
29061        let tree = crate::grammar_test_support::tree(source);
29062        crate::grammar_test_support::assert_function_like_export_class_head(
29063            &tree,
29064            source,
29065            "VirtualBased",
29066        );
29067        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29068        let actual = parse_cpp_declarations(source, "virtual-base.h");
29069        let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
29070        assert_eq!(
29071            declaration_expansion_inventory(&actual),
29072            declaration_expansion_inventory(&explicit)
29073        );
29074    }
29075
29076    #[test]
29077    #[hardwon("#6902: a semicolon-free macro envelope hid the records and restore after it")]
29078    fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
29079        let source = r###"struct Aggregate;
29080
29081#define NOARG
29082LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
29083#undef NOARG
29084
29085struct Context { int value; };
29086struct Visit { int value; };
29087
29088LIST_DECLS(static, deps, struct Aggregate *, unused)
29089LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
29090
29091static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29092
29093static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
29094static struct Context *restore(struct Aggregate *value) {
29095  return (struct Context *) value;
29096}
29097
29098static void caller(struct Context *ctx, struct Visit *visit) {
29099  target(ctx, 0, 0, visit); /* recovered prototype */
29100}
29101
29102static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29103  (void) ctx;
29104  (void) first;
29105  (void) second;
29106  (void) visit;
29107}
29108"###;
29109        let expanded = r###"struct Aggregate;
29110
29111#define NOARG
29112#undef NOARG
29113
29114struct Context { int value; };
29115struct Visit { int value; };
29116
29117
29118static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29119
29120static struct Context *restore(struct Aggregate *value);
29121static struct Context *restore(struct Aggregate *value) {
29122  return (struct Context *) value;
29123}
29124
29125static void caller(struct Context *ctx, struct Visit *visit) {
29126  target(ctx, 0, 0, visit); /* recovered prototype */
29127}
29128
29129static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29130  (void) ctx;
29131  (void) first;
29132  (void) second;
29133  (void) visit;
29134}
29135"###;
29136        let tree = crate::grammar_test_support::tree(source);
29137        assert!(
29138            tree.root_node().has_error(),
29139            "{}",
29140            tree.root_node().to_sexp()
29141        );
29142        let actual = parse_cpp_declarations(source, "expansion.h");
29143        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29144        assert_eq!(
29145            declaration_expansion_inventory(&actual),
29146            declaration_expansion_inventory(&explicit)
29147        );
29148    }
29149
29150    #[test]
29151    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29152    fn recovered_sentinel_alias_agrees_with_expansion() {
29153        let source = r###"namespace absl {
29154ABSL_NAMESPACE_BEGIN namespace container_internal {
29155
29156class raw_hash_set : public Base {
29157 public:
29158  using value_type = int;
29159
29160  template <class U,
29161            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29162  void insert(U value) { (void)value; }
29163
29164  struct InsertSlot {
29165    raw_hash_set& s;
29166  };
29167};
29168
29169}
29170ABSL_NAMESPACE_END
29171}"###;
29172        let expanded = r###"namespace absl {
29173namespace container_internal {
29174
29175class raw_hash_set : public Base {
29176 public:
29177  using value_type = int;
29178
29179  template <class U>
29180  void insert(U value) { (void)value; }
29181
29182  struct InsertSlot {
29183    raw_hash_set& s;
29184  };
29185};
29186
29187}
29188
29189}"###;
29190        let tree = crate::grammar_test_support::tree(source);
29191        assert!(
29192            tree.root_node().has_error(),
29193            "{}",
29194            tree.root_node().to_sexp()
29195        );
29196        let actual = parse_cpp_declarations(source, "expansion.h");
29197        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29198        assert_eq!(
29199            declaration_expansion_inventory(&actual),
29200            declaration_expansion_inventory(&explicit)
29201        );
29202    }
29203
29204    /// The structured alias facts the resolver and navigation read, for one
29205    /// declaration of `fq_name`.
29206    fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
29207        let units = file
29208            .declarations()
29209            .iter()
29210            .filter(|unit| unit.fq_name() == fq_name)
29211            .collect::<Vec<_>>();
29212        let [unit] = units.as_slice() else {
29213            panic!("expected one {fq_name}: {:#?}", file.declarations());
29214        };
29215        let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
29216        let fact = file
29217            .source_declaration_units
29218            .iter()
29219            .filter(|(_, linked)| linked == *unit)
29220            .map(|(declaration, _)| {
29221                facts
29222                    .declarations
29223                    .iter()
29224                    .find(|fact| fact.declaration == *declaration)
29225                    .expect("alias source fact")
29226            })
29227            .map(|fact| {
29228                (
29229                    &fact.alias_target,
29230                    &fact.alias_target_text,
29231                    &fact.lexical_path,
29232                    &fact.guard_requirements,
29233                    &fact.file_scope_alias,
29234                )
29235            })
29236            .collect::<Vec<_>>();
29237        let parents = file
29238            .children
29239            .iter()
29240            .filter(|(_, children)| children.contains(unit))
29241            .map(|(parent, _)| parent.fq_name())
29242            .collect::<Vec<_>>();
29243        format!(
29244            "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29245            unit.kind(),
29246            file.type_aliases.contains(unit),
29247            file.signatures.get(unit),
29248            file.signature_metadata.get(unit),
29249            file.cpp_template_metadata.get(unit),
29250        )
29251    }
29252
29253    #[test]
29254    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29255    fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29256        let source = r###"namespace absl {
29257ABSL_NAMESPACE_BEGIN namespace container_internal {
29258
29259class raw_hash_set : public Base {
29260 public:
29261  using value_type = int;
29262  template <class T>
29263  using rebind = Base<T>;
29264  using key_type = value_type;
29265#ifdef ABSL_HAVE_SLOT
29266  using slot_type = long;
29267#endif
29268
29269  template <class U,
29270            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29271  void insert(U value) { (void)value; }
29272
29273  struct InsertSlot {
29274    raw_hash_set& s;
29275  };
29276};
29277
29278}
29279ABSL_NAMESPACE_END
29280}"###;
29281        let expanded = source
29282            .replace("ABSL_NAMESPACE_BEGIN ", "")
29283            .replace("ABSL_NAMESPACE_END", "")
29284            .replace(
29285                ",\n            REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29286                ">",
29287            );
29288        assert!(
29289            crate::grammar_test_support::tree(source)
29290                .root_node()
29291                .has_error()
29292        );
29293        assert!(
29294            !crate::grammar_test_support::tree(&expanded)
29295                .root_node()
29296                .has_error()
29297        );
29298        let actual = parse_cpp_declarations(source, "expansion.h");
29299        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29300        assert_eq!(
29301            declaration_expansion_inventory(&actual),
29302            declaration_expansion_inventory(&explicit)
29303        );
29304        for (alias, text) in [
29305            ("value_type", "using value_type = int;"),
29306            ("rebind", "using rebind = Base<T>;"),
29307            ("key_type", "using key_type = value_type;"),
29308            ("slot_type", "using slot_type = long;"),
29309        ] {
29310            let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29311            // Kind, owner, target, template head, guard, and lexical scope all
29312            // agree with the explicitly written class.
29313            assert_eq!(
29314                sentinel_alias_facts(&actual, &fq_name),
29315                sentinel_alias_facts(&explicit, &fq_name),
29316                "{alias}"
29317            );
29318            // The range is the alias's own source text, line included.
29319            let unit = actual
29320                .declarations()
29321                .iter()
29322                .find(|unit| unit.fq_name() == fq_name)
29323                .unwrap();
29324            let start = source.find(text).unwrap();
29325            assert_eq!(
29326                actual.declaration_ranges(unit),
29327                [Range {
29328                    start_byte: start,
29329                    end_byte: start + text.len(),
29330                    start_line: source[..start].matches('\n').count() + 1,
29331                    end_line: source[..start].matches('\n').count() + 1,
29332                }],
29333                "{alias}"
29334            );
29335        }
29336        let rendered =
29337            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29338        assert!(
29339            rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29340                && rendered
29341                    .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29342            "the target resolves in the recovered class: {rendered}"
29343        );
29344        assert!(
29345            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29346                .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29347            "the guarded alias keeps its guard"
29348        );
29349    }
29350
29351    #[test]
29352    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29353    fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29354        // Without the class's displaced `};` the sentinel recovery cannot
29355        // prove where raw_hash_set ends, so it recovers no class at all.
29356        let source = r###"namespace absl {
29357ABSL_NAMESPACE_BEGIN namespace container_internal {
29358
29359class raw_hash_set : public Base {
29360 public:
29361  using value_type = int;
29362
29363  template <class U,
29364            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29365  void insert(U value) { (void)value; }
29366
29367}
29368ABSL_NAMESPACE_END
29369}"###;
29370        let parsed = parse_cpp_declarations(source, "unproven.h");
29371        let declarations = parsed.declarations();
29372        assert!(
29373            declarations
29374                .iter()
29375                .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29376            "no member may claim an owner the recovery did not prove: {declarations:#?}"
29377        );
29378        assert!(
29379            declarations
29380                .iter()
29381                .filter(|unit| unit.identifier() == "value_type")
29382                .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29383            "the alias must not become a field: {declarations:#?}"
29384        );
29385    }
29386
29387    #[test]
29388    #[hardwon("#6902: consecutive decorated export classes lost most of their members")]
29389    fn recovered_consecutive_export_classes_agree_with_expansion() {
29390        let source =
29391            function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
29392        let tree = crate::grammar_test_support::tree(&source);
29393        for name in ["Exception", "Invalid_Argument"] {
29394            crate::grammar_test_support::assert_function_like_export_class_head(
29395                &tree, &source, name,
29396            );
29397        }
29398        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29399        let actual = parse_cpp_declarations(&source, "expansion.h");
29400        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29401        assert_eq!(
29402            declaration_expansion_inventory(&actual),
29403            declaration_expansion_inventory(&explicit)
29404        );
29405    }
29406
29407    #[test]
29408    #[hardwon(
29409        "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
29410    )]
29411    fn standalone_field_list_agrees_with_expansion() {
29412        let source =
29413            "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
29414        let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
29415        let tree = crate::grammar_test_support::tree(source);
29416        assert!(
29417            crate::grammar_test_support::nodes(tree.root_node())
29418                .iter()
29419                .any(|node| node.kind() == "ERROR"
29420                    && node
29421                        .named_child(0)
29422                        .is_some_and(|child| child.kind() == "identifier")),
29423            "{}",
29424            tree.root_node().to_sexp()
29425        );
29426        let actual = parse_cpp_declarations(source, "expansion.h");
29427        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29428        assert_eq!(
29429            declaration_expansion_inventory(&actual),
29430            declaration_expansion_inventory(&explicit)
29431        );
29432    }
29433
29434    #[test]
29435    #[hardwon(
29436        "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
29437    )]
29438    fn recovered_constructor_parameters_are_not_fields() {
29439        let source = r#"namespace Botan {
29440class EXPORT(2,0) Key final {
29441public:
29442    Key(int value);
29443    void run();
29444};
29445class EXPORT(2,0) DL_Group final {
29446public:
29447    ATTRIBUTE("use factory") explicit DL_Group(const char* name);
29448    DL_Group(int value);
29449    int get_p() const;
29450};
29451}"#;
29452        let actual = parse_cpp_declarations(source, "constructors.h");
29453        let expanded = source
29454            .replace("EXPORT(2,0) ", "")
29455            .replace("ATTRIBUTE(\"use factory\") ", "");
29456        let explicit = parse_cpp_declarations(&expanded, "constructors.h");
29457        assert_eq!(
29458            declaration_expansion_inventory(&actual),
29459            declaration_expansion_inventory(&explicit)
29460        );
29461    }
29462}