Skip to main content

big_code_analysis_ast/node/
ancestors.rs

1//! [`Ancestors`], the chain of a node's ancestors a walker threads down
2//! to its predicates, and the [`SiblingCache`] and [`FoldCache`] it can
3//! lend with it.
4
5use std::cell::RefCell;
6use std::collections::HashMap;
7
8use super::Node;
9
10/// The chain of a node's ancestors, root first, as recorded by a walker
11/// that descended to that node.
12///
13/// A predicate that asks a node for its parent pays [`Node::parent`]'s
14/// `O(depth)` once per node, so `O(depth²)` over a deeply nested file,
15/// however few parent steps it takes (#1084). A walker that visits
16/// parents before children already holds the chain, and handing it down
17/// turns each step into a slice index — the upward counterpart of the
18/// downward flag propagation #1052 and #1062 used.
19///
20/// Callers that reached a node some other way pass
21/// [`Ancestors::unknown`], which climbs with [`Node::parent`]: the same
22/// answers at the original cost.
23#[derive(Clone, Copy, Debug)]
24pub struct Ancestors<'tree, 'chain> {
25    chain: Option<&'chain [Node<'tree>]>,
26    siblings: Option<&'chain SiblingCache<'tree>>,
27    folds: Option<&'chain FoldCache>,
28}
29
30impl<'tree, 'chain> Ancestors<'tree, 'chain> {
31    /// No chain is available; every query climbs with [`Node::parent`].
32    #[inline]
33    #[must_use]
34    pub const fn unknown() -> Self {
35        Self {
36            chain: None,
37            siblings: None,
38            folds: None,
39        }
40    }
41
42    /// `chain` lists every ancestor of the node about to be queried,
43    /// root first — so `chain.last()` is its parent and an empty chain
44    /// means the node is the root.
45    #[inline]
46    #[must_use]
47    pub const fn known(chain: &'chain [Node<'tree>]) -> Self {
48        Self {
49            chain: Some(chain),
50            siblings: None,
51            folds: None,
52        }
53    }
54
55    /// This chain, answering [`siblings_after`](Self::siblings_after) and
56    /// [`siblings_before`](Self::siblings_before) through `cache`, which
57    /// the walker that keeps the chain owns for the whole walk.
58    #[inline]
59    #[must_use]
60    pub const fn with_siblings(self, cache: &'chain SiblingCache<'tree>) -> Self {
61        Self {
62            siblings: Some(cache),
63            ..self
64        }
65    }
66
67    /// This chain, memoising `Ancestors::fold_down` through
68    /// `cache`, which the walker that keeps the chain owns for the whole
69    /// walk.
70    #[inline]
71    #[must_use]
72    pub const fn with_folds(self, cache: &'chain FoldCache) -> Self {
73        Self {
74            folds: Some(cache),
75            ..self
76        }
77    }
78
79    /// The state of a fold of `step` down `node`'s ancestry, leaving out
80    /// the `skip` ancestors nearest `node`.
81    ///
82    /// The fold starts at `init` above the root and, for each ancestor
83    /// `a` from the root down, applies `step(state, a, a's chain, below)`,
84    /// where `below` is the next node on the path to `node` — the ancestor
85    /// under `a`, or `node` itself under its parent. A predicate that
86    /// would otherwise climb every ancestor of every candidate node, and
87    /// so cost `O(candidates × depth)`, asks this instead (#1597).
88    ///
89    /// Under a walk that lent a [`FoldCache`], the state at each ancestor
90    /// is stored per depth, keyed by the ancestor's id, so each one is
91    /// folded once per walk and a query costs amortised `O(1)`. A node
92    /// fixes its own ancestry, so a stored state whose id matches the
93    /// chain is exact; a slot left by a node the walk has moved past holds
94    /// another id and is refolded. `step` must answer the same for every
95    /// call on one `lane` within a walk, and must not fold itself. The
96    /// state at `node` (`skip == 0`) depends on `node`, so it is folded
97    /// fresh. Without a known chain the ancestry is climbed with
98    /// [`Node::parent`] first, which is `O(depth²)` per query; only tests
99    /// and chainless callers reach that path, never the metrics walk.
100    pub(crate) fn fold_down(
101        self,
102        node: &Node<'tree>,
103        skip: usize,
104        lane: FoldLane,
105        init: u8,
106        step: impl Fn(u8, &Node<'tree>, Ancestors<'tree, 'chain>, &Node<'tree>) -> u8,
107    ) -> u8 {
108        let Some(chain) = self.chain else {
109            return fold_climbed(node, skip, init, step);
110        };
111        let end = chain.len().saturating_sub(skip);
112        let step_at = |state: u8, depth: usize| {
113            let outer = Ancestors {
114                chain: Some(&chain[..depth]),
115                siblings: self.siblings,
116                folds: None,
117            };
118            step(
119                state,
120                &chain[depth],
121                outer,
122                chain.get(depth + 1).unwrap_or(node),
123            )
124        };
125        match self.folds {
126            Some(cache) => cache.fold(lane, chain, end, init, step_at),
127            None => (0..end).fold(init, step_at),
128        }
129    }
130
131    /// The walk's per-node memo, when it lent a [`FoldCache`].
132    pub(crate) fn node_memo(self) -> Option<&'chain NodeMemo> {
133        self.folds.map(|cache| &cache.nodes)
134    }
135
136    /// Whether [`fold_down`](Self::fold_down) is memoised on this chain:
137    /// one a walk lent a [`FoldCache`] with. Otherwise every fold runs
138    /// from the root, so a predicate whose own climb is bounded takes that
139    /// instead.
140    pub(crate) fn memoises_folds(self) -> bool {
141        self.folds.is_some() && self.chain.is_some()
142    }
143
144    /// [`Ancestors::known`], but first checks that `chain` really is
145    /// `node`'s ancestry.
146    ///
147    /// The parity test below proves the walker's truncate/push rule on a
148    /// *replica* walker, so it cannot see `metrics_inner` itself
149    /// desynchronising — a `chain.push` moved ahead of the per-node
150    /// computes, say, or a `continue` inserted above the truncate.
151    /// [`Ancestors::parent`] trusts `chain.last()` unvalidated, so such a
152    /// drift would feed every predicate a wrong ancestor silently rather
153    /// than fail. Walkers that maintain a chain should construct through
154    /// here; [`Ancestors::known`] stays unchecked for the callers that
155    /// deliberately pair a chain with a foreign node.
156    ///
157    /// # Two checks, because the exact one is not affordable by default
158    ///
159    /// The invariant is "`chain.last()` **is** `node.parent()`", and
160    /// asking that outright costs [`Node::parent`]'s `O(depth)` — the
161    /// very lookup #1084 exists to remove. Per node, on every walk that
162    /// constructs a checked chain, it made every debug-build walk
163    /// `O(nodes × depth)` while the shipped walk is `O(nodes)`: a tax on
164    /// every `cargo test`, worst on the deep-nesting regression tests
165    /// that exist to pin the shipped walk's linearity (#1122). It now
166    /// runs only under `--cfg chain_audit`, which `make chain-audit` and
167    /// the CI lane of the same name set — and there as a plain
168    /// `assert_eq!`, so the audit has teeth in a release profile too.
169    ///
170    /// What stays on in every debug build is an `O(1)` *consequence* of
171    /// that invariant: a parent's byte span contains its child's, and no
172    /// node is its own parent. Strictly weaker — a grandparent contains
173    /// the node as well — but it is shaped to the two ways a walker
174    /// desynchronises. A `push` moved ahead of the per-node computes
175    /// leaves `chain.last() == node`; a dropped `truncate` leaves the
176    /// previous subtree's path, whose last entry is disjoint from the
177    /// node that follows it. Both trip here, on the first node that
178    /// shows them, at four integer comparisons.
179    #[must_use]
180    pub fn checked(chain: &'chain [Node<'tree>], node: &Node<'tree>) -> Self {
181        // Opt-in only: `Node::parent` restarts at the root, so this is
182        // the `O(nodes × depth)` walk described above.
183        #[cfg(chain_audit)]
184        assert_eq!(
185            chain.last().map(Node::id),
186            node.parent().map(|parent| parent.id()),
187            "ancestor chain desynchronised on a {} node",
188            node.kind()
189        );
190        debug_assert!(
191            chain.last().is_none_or(|parent| {
192                parent.id() != node.id()
193                    && parent.start_byte() <= node.start_byte()
194                    && node.end_byte() <= parent.end_byte()
195            }),
196            "ancestor chain desynchronised on a {} node: chain.last() neither \
197             contains it nor differs from it",
198            node.kind()
199        );
200        Self::known(chain)
201    }
202
203    /// How far the node this chain describes sits from the root, or
204    /// `None` when no chain is known — deriving it then would cost the
205    /// [`Node::parent`] climb the chain exists to remove.
206    #[inline]
207    pub fn depth(self) -> Option<usize> {
208        self.chain.map(<[Node<'tree>]>::len)
209    }
210
211    /// The chain of `node`'s children, `self` being `node`'s own: a copy
212    /// extended by `node`, for a predicate that looks into a node the walk
213    /// has not descended into yet. `None` without a known chain, where
214    /// [`Ancestors::unknown`] gives the same answers by climbing.
215    pub(crate) fn children_chain(self, node: &Node<'tree>) -> Option<Vec<Node<'tree>>> {
216        self.chain
217            .map(|chain| [chain, std::slice::from_ref(node)].concat())
218    }
219
220    /// `node`'s parent.
221    #[inline]
222    #[must_use]
223    pub fn parent(self, node: &Node<'tree>) -> Option<Node<'tree>> {
224        match self.chain {
225            Some(chain) => chain.last().copied(),
226            None => node.parent(),
227        }
228    }
229
230    /// Whether `node`'s parent has kind id `kind`.
231    ///
232    /// The compound-leaf guard of `.claude/rules/grammar-dispatch.md`
233    /// section 5 asks this and nothing else: a delimiter or keyword
234    /// token is suppressed *only* directly under the construct that
235    /// owns it, never under an arbitrary ancestor. Eleven dispatch
236    /// arms across `getter`, `checker` and the metric walkers spelled
237    /// it out as `.parent(node).is_some_and(|p| p.kind_id() == X as
238    /// u16)`, which wraps onto four rustfmt lines inside a match guard
239    /// and buries the question under the plumbing; #1314 added six more
240    /// and folded all seventeen onto this. Three sites spelling their
241    /// binding `|parent|` were left alone and would each fit:
242    /// `impl_is_else_if_parent_clause!` (`src/checker.rs`),
243    /// `is_stabby_lambda_body` (`src/checker/ruby.rs`) and
244    /// `is_useful_comment` (`src/checker/rust.rs`, a let-chain).
245    ///
246    /// A `false` when `node` has no parent is the answer every one of
247    /// those call sites wants: a root node's token is not inside the
248    /// construct, so it is not suppressed.
249    #[inline]
250    #[must_use]
251    pub fn parent_has_kind(self, node: &Node<'tree>, kind: u16) -> bool {
252        self.parent(node).is_some_and(|p| p.kind_id() == kind)
253    }
254
255    /// The sibling immediately before `node`, or `None` when `node` is
256    /// its parent's first child or has no parent.
257    ///
258    /// `tree_sitter`'s own `prev_sibling` resolves the parent first
259    /// (`ts_node__prev_sibling` opens with `ts_node_parent`), so it
260    /// carries the same `O(depth)` cost [`Node::parent`] does. With a
261    /// known chain the parent is free and what remains is a cursor walk
262    /// over the siblings — or, for a wide parent the walk recorded in its
263    /// [`SiblingCache`], a binary search, since a walk over every child
264    /// of a Kotlin string's `$name` run was quadratic in it (#1584).
265    #[inline]
266    #[must_use]
267    pub fn previous_sibling(self, node: &Node<'tree>) -> Option<Node<'tree>> {
268        let Some(chain) = self.chain else {
269            return node.previous_sibling();
270        };
271        let parent = chain.last()?;
272        if let Lookup::Wide(children, index) = self.lookup(node) {
273            return match index {
274                Some(index) => children[..index].last().copied(),
275                None => node.previous_sibling(),
276            };
277        }
278        let mut previous = None;
279        for child in parent.children() {
280            if child.id() == node.id() {
281                return previous;
282            }
283            previous = Some(child);
284        }
285        // `node` is not among `parent.children()`, so this chain does
286        // not describe `node`. Answering `None` would claim "no
287        // previous sibling", which is a different (and wrong) answer;
288        // fall back to the authoritative lookup instead.
289        node.previous_sibling()
290    }
291
292    /// `node`'s ancestors, nearest first, each paired with *its* own
293    /// ancestry so a predicate applied to an ancestor stays as cheap as
294    /// one applied to `node`.
295    #[inline]
296    #[must_use]
297    pub fn iter(self, node: &Node<'tree>) -> AncestorIter<'tree, 'chain> {
298        match self.chain {
299            Some(chain) => AncestorIter::Chain(chain, self.siblings),
300            None => AncestorIter::Climb(node.parent()),
301        }
302    }
303
304    /// The siblings after `node`, nearest first: what
305    /// [`Node::children_after`] yields on `node`'s parent, and nothing
306    /// for the root. `self` is `node`'s chain.
307    ///
308    /// The seek [`Node::children_after`] runs is `O(index)` over a flat
309    /// child array, so a walk that asks of every child of a wide
310    /// `ERROR` is quadratic in it (#1584). A walk that lends a
311    /// [`SiblingCache`] has already collected a wide parent's children,
312    /// and the lookup is a binary search among them.
313    pub fn siblings_after(
314        self,
315        node: &Node<'tree>,
316    ) -> impl Iterator<Item = Node<'tree>> + use<'tree, 'chain> {
317        let (wide, narrow) = match self.lookup(node) {
318            Lookup::Wide(children, index) => (index.map(|index| &children[index + 1..]), None),
319            Lookup::Narrow(parent) => (None, parent.map(|parent| parent.children_after(node))),
320        };
321        let wide = wide.into_iter().flatten().copied();
322        wide.chain(narrow.into_iter().flatten())
323    }
324
325    /// The siblings before `node`, nearest first: what
326    /// [`Node::children_before`] yields on `node`'s parent, and nothing
327    /// for the root. `self` is `node`'s chain; see
328    /// [`siblings_after`](Self::siblings_after).
329    pub fn siblings_before(
330        self,
331        node: &Node<'tree>,
332    ) -> impl Iterator<Item = Node<'tree>> + use<'tree, 'chain> {
333        let (wide, narrow) = match self.lookup(node) {
334            Lookup::Wide(children, index) => (index.map(|index| &children[..index]), None),
335            Lookup::Narrow(parent) => (None, parent.map(|parent| parent.children_before(node))),
336        };
337        let wide = wide.into_iter().flatten().rev().copied();
338        wide.chain(narrow.into_iter().flatten())
339    }
340
341    // Where `node`'s siblings are read from: its parent's children as the
342    // walk recorded them and `node`'s index among them, or the parent to
343    // seek in when the walk recorded none.
344    fn lookup(self, node: &Node<'tree>) -> Lookup<'tree, 'chain> {
345        let parent = self.parent(node);
346        let recorded = match (self.siblings, self.chain, parent) {
347            (Some(cache), Some(chain), Some(parent)) => {
348                let recorded = cache.children(&parent, chain.len() - 1);
349                // A walk that stopped recording would answer the same,
350                // only quadratically, which no metric value shows.
351                debug_assert!(
352                    recorded.is_some() || parent.child_count() <= WIDE_PARENT,
353                    "a walk lending a SiblingCache must record every wide node on its \
354                     chain (#1584), but not this {}",
355                    parent.kind()
356                );
357                recorded
358            }
359            _ => None,
360        };
361        match recorded {
362            Some(children) => Lookup::Wide(children, position_by_start(children, node)),
363            None => Lookup::Narrow(parent),
364        }
365    }
366}
367
368enum Lookup<'tree, 'chain> {
369    Wide(&'chain [Node<'tree>], Option<usize>),
370    Narrow(Option<Node<'tree>>),
371}
372
373/// Children past which a walk records a node's children for sibling
374/// lookups. A seek over this many is a short scan however the grammar
375/// stores them, so recording a narrower node would copy it for nothing.
376///
377/// `pub` and `#[doc(hidden)]` only so the wide-parent fixtures in this
378/// crate's and `big-code-analysis`'s tests size themselves from it: a
379/// hand-copied width would silently go narrow if this were raised.
380#[doc(hidden)]
381pub const WIDE_PARENT: usize = 64;
382
383// `node`'s index in `children`, a parent's children in order. Their start
384// bytes never decrease, so a binary search lands on the first child
385// starting where `node` does; only zero-width children, which recovery
386// inserts, can share that byte with it.
387fn position_by_start(children: &[Node], node: &Node) -> Option<usize> {
388    let first = children.partition_point(|child| child.start_byte() < node.start_byte());
389    children[first..]
390        .iter()
391        .take_while(|child| child.start_byte() == node.start_byte())
392        .position(|child| child.id() == node.id())
393        .map(|offset| first + offset)
394}
395
396/// The children of each wide node on a walk's chain, recorded as the
397/// walk pushes them, so that [`Ancestors::siblings_after`] and
398/// [`Ancestors::siblings_before`] find a sibling in a flat `ERROR`
399/// without a scan from its start (#1584).
400///
401/// One slot per depth, keyed by the node's id. A walk descends into a
402/// node only after pushing its children, so every wide node on the
403/// chain has its slot; a slot left behind by a node the walk has moved
404/// past holds another id and is not read. Recording copies what the
405/// walk already holds, once per wide node.
406#[derive(Debug, Default)]
407pub struct SiblingCache<'tree>(Vec<(usize, Vec<Node<'tree>>)>);
408
409impl<'tree> SiblingCache<'tree> {
410    /// Records `children`, in order, as the children of `node`, which
411    /// sits at `depth` in the walk's chain. A node of no more than a
412    /// short scan's children is not recorded.
413    pub fn record(
414        &mut self,
415        node: &Node<'tree>,
416        depth: usize,
417        children: impl IntoIterator<Item = Node<'tree>>,
418    ) {
419        if node.child_count() <= WIDE_PARENT {
420            return;
421        }
422        if self.0.len() <= depth {
423            self.0.resize_with(depth + 1, Default::default);
424        }
425        // An unused slot's id is 0, which no node has: tree-sitter's id
426        // is the address of the node's subtree.
427        let (id, recorded) = &mut self.0[depth];
428        *id = node.id();
429        recorded.clear();
430        recorded.extend(children);
431    }
432
433    // `parent`'s children, when the walk recorded them at `depth`.
434    fn children(&self, parent: &Node<'tree>, depth: usize) -> Option<&[Node<'tree>]> {
435        self.0
436            .get(depth)
437            .filter(|(id, _)| *id == parent.id())
438            .map(|(_, children)| children.as_slice())
439    }
440}
441
442// The deepest depth at or above `from` whose slot still holds the state of
443// the ancestor the chain has there, or 0 when none does: a node fixes its
444// ancestry, so a matching id is an exact state.
445fn deepest_stored(slots: &[(usize, u8)], chain: &[Node<'_>], from: usize) -> usize {
446    (1..=from)
447        .rev()
448        .find(|&depth| slots[depth].0 == chain[depth].id())
449        .unwrap_or(0)
450}
451
452// `Ancestors::fold_down` for a caller that holds no chain: the ancestry is
453// climbed first and folded unmemoised.
454fn fold_climbed<'tree, 'chain>(
455    node: &Node<'tree>,
456    skip: usize,
457    init: u8,
458    step: impl Fn(u8, &Node<'tree>, Ancestors<'tree, 'chain>, &Node<'tree>) -> u8,
459) -> u8
460where
461    'tree: 'chain,
462{
463    let climbed = climbed_chain(node);
464    let end = climbed.len().saturating_sub(skip);
465    (0..end).fold(init, |state, depth| {
466        let below = climbed.get(depth + 1).unwrap_or(node);
467        step(state, &climbed[depth], Ancestors::unknown(), below)
468    })
469}
470
471// `node`'s ancestors, root first, climbed with `Node::parent` for a caller
472// that holds no chain. tree-sitter finds a parent by descending from the
473// root, so the climb is `O(depth²)`.
474fn climbed_chain<'tree>(node: &Node<'tree>) -> Vec<Node<'tree>> {
475    let mut climbed: Vec<_> = std::iter::successors(node.parent(), Node::parent).collect();
476    climbed.reverse();
477    climbed
478}
479
480/// Which memoised fold a [`FoldCache`] slot holds. A language may run
481/// more than one fold over the same walk, and two folds' states must not
482/// share a slot.
483#[derive(Clone, Copy, Debug, PartialEq, Eq)]
484pub(crate) enum FoldLane {
485    /// Whether an ancestor holds a doc comment in a function body.
486    DocInBody,
487    /// A positional language's declaration scope: Go's package level,
488    /// Ruby's YARD namespace. One lane serves both because a walk parses
489    /// one language.
490    DocScope,
491    /// Python: the climb from a node through the parentheses above it to
492    /// a bare string statement (#1610).
493    BareStringClimb,
494}
495
496impl FoldLane {
497    /// How many lanes a [`FoldCache`] holds; one per variant above.
498    const COUNT: usize = 3;
499}
500
501/// The per-depth states of the walk's memoised folds, for
502/// `Ancestors::fold_down` (#1597).
503///
504/// One slot per depth and lane, keyed by the id of the ancestor at that
505/// depth: the state folded over every ancestor above it. Interior
506/// mutability, because [`Ancestors`] lends it shared, as it lends a
507/// [`SiblingCache`]; a fold never re-enters itself, so the borrow is
508/// never contended.
509#[derive(Debug, Default)]
510pub struct FoldCache {
511    lanes: RefCell<[Vec<(usize, u8)>; FoldLane::COUNT]>,
512    nodes: NodeMemo,
513}
514
515/// Answers about a node's own subtree, memoised for one walk and keyed
516/// by node id, which is unique within a tree: Doxygen's "does this
517/// preprocessor conditional hold an entry", asked of every conditional
518/// a doc comment's stretch meets. Unlike a [`FoldLane`] it is not tied to
519/// the chain, so an answer stored for one sibling subtree serves every
520/// later question about it (#1613). It holds one question; a second
521/// would need a key of its own.
522#[derive(Debug, Default)]
523pub(crate) struct NodeMemo(RefCell<HashMap<usize, bool>>);
524
525impl NodeMemo {
526    /// The stored answer for `node`, if any.
527    pub(crate) fn get(&self, node: &Node<'_>) -> Option<bool> {
528        self.0.borrow().get(&node.id()).copied()
529    }
530
531    /// Stores `answer` for `node`.
532    pub(crate) fn set(&self, node: &Node<'_>, answer: bool) {
533        self.0.borrow_mut().insert(node.id(), answer);
534    }
535}
536
537impl FoldCache {
538    // The state after folding `step(state, depth)` over `chain[..end]`,
539    // reusing and storing the per-depth states on `lane`.
540    fn fold(
541        &self,
542        lane: FoldLane,
543        chain: &[Node<'_>],
544        end: usize,
545        init: u8,
546        step: impl Fn(u8, usize) -> u8,
547    ) -> u8 {
548        let mut lanes = self.lanes.borrow_mut();
549        let slots = &mut lanes[lane as usize];
550        // `slots[d]` is the state at `chain[d]`: folded over `chain[..d]`.
551        // Depth 0 is `init`; the state at `node` itself is never stored.
552        let memo_end = end.min(chain.len().saturating_sub(1));
553        if slots.len() <= memo_end {
554            // Id 0 marks an empty slot: a tree-sitter node id is its
555            // address, never 0.
556            slots.resize(memo_end + 1, (0, 0));
557        }
558        let mut depth = deepest_stored(slots, chain, memo_end);
559        let mut state = if depth == 0 { init } else { slots[depth].1 };
560        while depth < memo_end {
561            state = step(state, depth);
562            depth += 1;
563            slots[depth] = (chain[depth].id(), state);
564        }
565        if end > memo_end {
566            state = step(state, memo_end);
567        }
568        state
569    }
570}
571
572/// Ancestor iterator returned by [`Ancestors::iter`], nearest first.
573pub enum AncestorIter<'tree, 'chain> {
574    /// The not-yet-yielded prefix of a known chain, and the walk's
575    /// [`SiblingCache`] if it lent one. The prefix's last element is
576    /// the next ancestor, and the prefix before it is that ancestor's
577    /// own chain — so splitting from the back hands out both at once.
578    Chain(&'chain [Node<'tree>], Option<&'chain SiblingCache<'tree>>),
579    /// The next ancestor to yield, reached by climbing.
580    Climb(Option<Node<'tree>>),
581}
582
583impl<'tree, 'chain> Iterator for AncestorIter<'tree, 'chain> {
584    type Item = (Node<'tree>, Ancestors<'tree, 'chain>);
585
586    fn next(&mut self) -> Option<Self::Item> {
587        match self {
588            Self::Chain(remaining, siblings) => {
589                let (&nearest, above) = remaining.split_last()?;
590                *remaining = above;
591                let ancestors = Ancestors {
592                    chain: Some(above),
593                    siblings: *siblings,
594                    folds: None,
595                };
596                Some((nearest, ancestors))
597            }
598            Self::Climb(next) => {
599                let nearest = (*next)?;
600                *next = nearest.parent();
601                Some((nearest, Ancestors::unknown()))
602            }
603        }
604    }
605}
606
607#[cfg(test)]
608mod tests {
609    use super::*;
610    use crate::node::Tree;
611
612    // The ids of `nodes`, so a failing comparison prints which differ.
613    #[cfg(feature = "perl")]
614    fn ids<'a>(nodes: impl Iterator<Item = Node<'a>>) -> Vec<usize> {
615        nodes.map(|node| node.id()).collect()
616    }
617
618    // `parent`'s children through `child(i)`, the raw walk that neither
619    // the seek nor the recorded array is built on.
620    #[cfg(feature = "perl")]
621    fn raw_children(parent: &Node) -> Vec<usize> {
622        let raw = parent.as_tree_sitter();
623        (0..raw.child_count() as u32)
624            .filter_map(|i| raw.child(i))
625            .map(|child| child.id())
626            .collect()
627    }
628
629    // `my @r = (` and a body never closed: recovery makes the root an
630    // `ERROR` whose children are the body's tokens, one flat array of
631    // well over `WIDE_PARENT` of them.
632    #[cfg(feature = "perl")]
633    fn flat_error() -> Tree {
634        let source = format!("my @r = ({}", "{\nlast,\n$a && $b,\n".repeat(20));
635        Tree::new::<crate::langs::PerlCode>(source.as_bytes())
636    }
637
638    /// `fold_down` answers the same through all three of its paths: the
639    /// walk's memoised [`FoldCache`], a known chain with no cache, and the
640    /// chainless climb. Queried in walk order, as the metrics walk queries
641    /// it, and twice per node so the second query is answered from the
642    /// stored slots (#1597). The step depends on order, on `below` and on
643    /// the parent its `outer` chain names, so a slot read at the wrong
644    /// depth, a climb in the wrong direction or an `outer` cut at the wrong
645    /// place changes the answer. The climb hands `Ancestors::unknown()`,
646    /// whose `parent` falls back to `Node::parent`, so all three agree only
647    /// when the chains are right.
648    #[cfg(feature = "rust")]
649    #[test]
650    fn fold_down_agrees_across_its_cache_chain_and_climb_paths() {
651        let tree = Tree::new::<crate::langs::RustCode>(
652            b"mod a {\n    mod b {\n        fn f() { let x = (1 + 2) * 3; }\n    }\n    fn g() {}\n}\nfn h() { if true { 4 } else { 5 }; }\n",
653        );
654        let step = |state: u8, ancestor: &Node, outer: Ancestors, below: &Node| {
655            let parent = outer
656                .parent(ancestor)
657                .map_or(0, |parent| parent.kind_id().to_le_bytes()[0]);
658            state
659                .wrapping_mul(31)
660                .wrapping_add(ancestor.kind_id().to_le_bytes()[0])
661                .wrapping_add(below.kind_id().to_le_bytes()[0].rotate_left(3))
662                .wrapping_add(parent.rotate_left(5))
663        };
664        let cache = FoldCache::default();
665        let mut stack = vec![(tree.get_root(), 0_usize)];
666        let mut chain: Vec<Node> = Vec::new();
667        let mut queried = 0_usize;
668        while let Some((node, depth)) = stack.pop() {
669            chain.truncate(depth);
670            for skip in 0..=3 {
671                let cached = |c: &[Node<'_>]| {
672                    Ancestors::known(c).with_folds(&cache).fold_down(
673                        &node,
674                        skip,
675                        FoldLane::DocInBody,
676                        7,
677                        step,
678                    )
679                };
680                let first = cached(&chain);
681                let again = cached(&chain);
682                let uncached =
683                    Ancestors::known(&chain).fold_down(&node, skip, FoldLane::DocInBody, 7, step);
684                let climbed =
685                    Ancestors::unknown().fold_down(&node, skip, FoldLane::DocInBody, 7, step);
686                assert_eq!(
687                    (first, again, uncached),
688                    (climbed, climbed, climbed),
689                    "{} at depth {depth}, skip {skip}",
690                    node.kind()
691                );
692                queried += 1;
693            }
694            chain.push(node);
695            let children: Vec<_> = node.children().collect();
696            stack.extend(children.into_iter().rev().map(|child| (child, depth + 1)));
697        }
698        assert!(
699            queried > 100,
700            "the fixture must exercise a real walk: {queried}"
701        );
702    }
703
704    /// A recorded wide parent answers every child's siblings, and its
705    /// previous sibling, from the recorded array, and answers exactly
706    /// what the raw walk does on either side of it — the `ERROR` whose
707    /// flat array made the seek `O(index)` (#1584). A node that is no
708    /// child of it gets nothing.
709    #[cfg(feature = "perl")]
710    #[test]
711    fn siblings_of_a_recorded_wide_parent_match_the_child_walk() {
712        let tree = flat_error();
713        let root = tree.get_root();
714        assert!(root.is_error(), "the fixture's tail must fail to parse");
715        assert!(root.child_count() > WIDE_PARENT, "the ERROR must be wide");
716
717        let mut cache = SiblingCache::default();
718        cache.record(&root, 0, root.children());
719        let chain = [root];
720        let ancestors = Ancestors::known(&chain).with_siblings(&cache);
721        let all = raw_children(&root);
722
723        let (mut observed, mut expected) = (Vec::new(), Vec::new());
724        for (index, child) in root.children().enumerate() {
725            let recorded_index = match ancestors.lookup(&child) {
726                Lookup::Wide(_, index) => index,
727                Lookup::Narrow(_) => None,
728            };
729            observed.push((
730                index,
731                recorded_index,
732                ids(ancestors.siblings_after(&child)),
733                ids(ancestors.siblings_before(&child)),
734                ancestors.previous_sibling(&child).map(|node| node.id()),
735            ));
736            expected.push((
737                index,
738                Some(index),
739                all[index + 1..].to_vec(),
740                all[..index].iter().rev().copied().collect(),
741                index.checked_sub(1).map(|previous| all[previous]),
742            ));
743        }
744        assert_eq!(observed, expected);
745
746        // A grandchild starting where its parent does, so the search
747        // reaches it and only the id comparison turns it away.
748        let stray = root
749            .children()
750            .find_map(|child| {
751                child
752                    .child(0)
753                    .filter(|g| g.start_byte() == child.start_byte())
754            })
755            .expect("the fixture nests a node at its parent's start");
756        assert!(matches!(ancestors.lookup(&stray), Lookup::Wide(_, None)));
757        assert_eq!(ancestors.siblings_after(&stray).count(), 0);
758        assert_eq!(ancestors.siblings_before(&stray).count(), 0);
759        // `previous_sibling` falls back to the node's own lookup instead,
760        // which finds a stray's real previous sibling: `$b`'s is the `&&`
761        // of its own chain, not anything in the root.
762        let operand = root
763            .children()
764            .find_map(|child| child.child(2))
765            .expect("the fixture holds `$a && $b`");
766        let previous = ancestors
767            .previous_sibling(&operand)
768            .expect("the fallback finds the `&&`");
769        assert_eq!(previous.kind(), "&&");
770    }
771
772    /// A narrow parent is never recorded, and its siblings come from
773    /// the seek — with or without a cache lent.
774    #[cfg(feature = "perl")]
775    #[test]
776    fn a_narrow_parent_is_sought_not_recorded() {
777        let tree = flat_error();
778        let root = tree.get_root();
779        let chain = [root];
780        let narrow = root
781            .children()
782            .find(|child| (2..=WIDE_PARENT).contains(&child.child_count()))
783            .expect("the fixture holds a narrow node with several children");
784        let mut cache = SiblingCache::default();
785        cache.record(&narrow, 1, narrow.children());
786        assert!(cache.0.is_empty(), "a narrow node must not be recorded");
787
788        let narrow_chain = [root, narrow];
789        let all = raw_children(&narrow);
790        for ancestors in [
791            Ancestors::known(&narrow_chain).with_siblings(&cache),
792            Ancestors::known(&narrow_chain),
793        ] {
794            let first = narrow.child(0).expect("narrow node has children");
795            assert!(matches!(ancestors.lookup(&first), Lookup::Narrow(Some(_))));
796            assert_eq!(ids(ancestors.siblings_after(&first)), all[1..]);
797        }
798        // Without a cache a wide parent is sought too.
799        let first = root.child(0).expect("root has children");
800        assert!(matches!(
801            Ancestors::known(&chain).lookup(&first),
802            Lookup::Narrow(Some(_))
803        ));
804    }
805
806    /// A walk that lends a cache but did not record a wide node on its
807    /// chain would answer correctly and quadratically; the assertion is
808    /// what makes that visible to every test that walks one.
809    #[cfg(all(feature = "perl", debug_assertions))]
810    #[test]
811    #[should_panic(expected = "must record every wide node on its chain")]
812    fn an_unrecorded_wide_parent_trips_the_walk_assertion() {
813        let tree = flat_error();
814        let root = tree.get_root();
815        let cache = SiblingCache::default();
816        let chain = [root];
817        let first = root.child(0).expect("root has children");
818        let _ = Ancestors::known(&chain)
819            .with_siblings(&cache)
820            .lookup(&first);
821    }
822
823    /// The binary search finds every child at its index, including a
824    /// zero-width one: recovery inserts it at the byte its successor
825    /// starts at, so only the id tells the two apart. The Kotlin
826    /// fixture's `vararg: Int` holds one.
827    #[cfg(feature = "kotlin")]
828    #[test]
829    fn position_by_start_finds_a_zero_width_child() {
830        let tree = Tree::new::<crate::langs::KotlinCode>(b"fun f(vararg: Int, b: Int) {}");
831        let mut zero_width = 0;
832        for node in tree.get_root().preorder() {
833            let children: Vec<_> = node.children().collect();
834            for (index, child) in children.iter().enumerate() {
835                zero_width += usize::from(child.start_byte() == child.end_byte());
836                assert_eq!(position_by_start(&children, child), Some(index));
837            }
838        }
839        assert!(
840            zero_width > 0,
841            "fixture must hold an inserted zero-width child"
842        );
843    }
844}