Skip to main content

big_code_analysis_ast/
node.rs

1// Metric counts (token, function, branch, argument, etc.) are stored as
2// `usize` and crossed with `f64` averages, ratios, and Halstead scores
3// across the cyclomatic / MI / Halstead computations. The `usize as f64`
4// and `f64 as usize` casts are intentional and snapshot-anchored — every
5// site is bounded by the count it came from. Allowing the lints at the
6// module level keeps the metric arithmetic legible.
7#![allow(
8    clippy::cast_precision_loss,
9    clippy::cast_possible_truncation,
10    clippy::cast_sign_loss
11)]
12
13//! Tree-sitter wrappers: `Tree`, [`Node`], [`Ancestors`], and the cursors.
14
15mod parser_cache;
16
17use tree_sitter::Node as OtherNode;
18use tree_sitter::Tree as OtherTree;
19use tree_sitter::TreeCursor;
20
21use crate::checker::Checker;
22use crate::traits::{LanguageInfo, Search};
23
24use parser_cache::parse_on_scratch_parser;
25
26// Sibling lookups that resolved the parent from the node, on this
27// thread.
28//
29// [`Node::previous_sibling`] answers exactly what the chain-based
30// [`Ancestors::previous_sibling`] does, so no assertion on a metric
31// value can tell a walk that uses one from a walk that uses the
32// other — only the cost differs, and only on a deep tree. #1096 took
33// the last of these out of the metric bodies and #1100 out of the
34// `exclude_tests` prune; the counter is what makes putting one back a
35// test failure rather than a silent quadratic.
36crate::observation::counter!(
37    node_resolved_sibling_lookups,
38    cfg(any(test, feature = "test-support"))
39);
40
41// Child scans that built their own `TreeCursor`, on this thread.
42//
43// [`Node::children_with`] yields exactly what [`Node::children`] yields
44// — it exists to reuse one cursor across a traversal instead of
45// heap-allocating and freeing one per visited node, and no assertion on
46// a metric value can tell the two apart. #1112 moved the per-node
47// traversals that could hoist a cursor onto it; the counter is what
48// makes moving one back a test failure rather than a silent allocation
49// per node.
50//
51// All five consumers are guarded: `preorder` and `act_on_node` here,
52// `metrics::npa::python` and the suppression DFS through a `metrics()`
53// / `suppression_markers` call, and `output::dump`'s renderer from that
54// module's own tests. The accessor is `pub` (not `pub(super)`)
55// precisely so the last one can be asserted from where it lives — see
56// `crate::observation`.
57crate::observation::counter!(child_scan_cursors, cfg(any(test, feature = "test-support")));
58
59/// A parsed source tree wrapping a [`tree_sitter::Tree`].
60///
61/// The "open parse seam" (see issue #251) is reached by external
62/// callers through `big_code_analysis::Ast::from_tree_sitter`, which accepts a
63/// caller-built `tree_sitter::Tree` directly; this wrapper stays
64/// internal so the metric walker is the only thing that observes it.
65#[derive(Clone, Debug)]
66pub(crate) struct Tree(OtherTree);
67
68impl Tree {
69    /// # Panics
70    ///
71    /// When `T`'s per-language Cargo feature is disabled in this build.
72    ///
73    /// [`AnyParser::parse`](crate::langs::AnyParser::parse) and
74    /// [`AnyParser::from_tree`](crate::langs::AnyParser::from_tree)
75    /// cfg-gate each `LANG::*` arm and return
76    /// `Err(MetricsError::LanguageDisabled)` before reaching here, so
77    /// every path through `big_code_analysis::Ast` is safe. Since
78    /// #1376, though, `Parser<T>` and [`ParserTrait::new`] are `pub` in
79    /// this crate, so a caller that names a `*Parser` alias directly
80    /// bypasses that gate — check
81    /// [`LANG::is_enabled`](crate::LANG::is_enabled) first, or go
82    /// through `AnyParser` and match on the error.
83    pub(crate) fn new<T: LanguageInfo>(code: &[u8]) -> Self {
84        let language = T::lang().get_ts_language().expect(
85            "invariant: the caller checked LANG::is_enabled, or reached this through AnyParser",
86        );
87        Self(parse_on_scratch_parser(&language, code))
88    }
89
90    pub(crate) fn from_ts_tree(tree: OtherTree) -> Self {
91        Self(tree)
92    }
93
94    pub(crate) fn get_root(&self) -> Node<'_> {
95        Node(self.0.root_node())
96    }
97
98    pub(crate) fn as_ts_tree(&self) -> &OtherTree {
99        &self.0
100    }
101}
102
103/// An `AST` node.
104///
105/// The inner [`tree_sitter::Node`] is reached through
106/// [`Node::as_tree_sitter`] for advanced use cases that need direct
107/// access to the underlying tree-sitter API; the field itself is
108/// private so a `tree-sitter` version bump cannot silently reshape
109/// this struct's public layout.
110#[derive(Clone, Copy, Debug)]
111pub struct Node<'a>(OtherNode<'a>);
112
113impl<'a> Node<'a> {
114    /// Returns the underlying [`tree_sitter::Node`] for callers that
115    /// want to drive their own traversal alongside the metric walker.
116    ///
117    /// `tree_sitter::Node` is [`Copy`], so the node is returned by
118    /// value. The returned node borrows the same source-tree lifetime
119    /// as `self`.
120    ///
121    /// The `tree-sitter` re-export this exposes is *value-not-stable*:
122    /// the underlying pin may bump in any minor release, so node shape
123    /// and node-kind ids are not part of this crate's stability
124    /// contract (see the [`tree_sitter`] re-export
125    /// note in the crate root).
126    #[must_use]
127    #[inline]
128    pub fn as_tree_sitter(&self) -> OtherNode<'a> {
129        self.0
130    }
131
132    /// Checks if a node represents a syntax error or contains any syntax errors
133    /// anywhere within it.
134    ///
135    /// Transitive, which is the trap: asking it of a construct to decide
136    /// whether that construct's *own* shape is readable also answers `true`
137    /// for an error arbitrarily deep inside one of its children. Where the
138    /// question is about a node's own slot sequence, ask [`is_error`] /
139    /// [`is_missing`] of each slot instead (#1381).
140    ///
141    /// [`is_error`]: Self::is_error
142    /// [`is_missing`]: Self::is_missing
143    #[must_use]
144    pub fn has_error(&self) -> bool {
145        self.0.has_error()
146    }
147
148    /// Whether this node *is* the `ERROR` node, as opposed to
149    /// [`has_error`](Self::has_error)'s "is, or contains one".
150    #[must_use]
151    #[inline]
152    pub fn is_error(&self) -> bool {
153        self.0.is_error()
154    }
155
156    /// Whether the parser inserted this node during error recovery: a
157    /// zero-width token the source does not contain, standing in for one
158    /// the grammar required.
159    ///
160    /// A sibling of [`is_error`](Self::is_error) rather than a synonym —
161    /// a MISSING token is not an `ERROR` node, but it occupies a child
162    /// slot just the same, so a rule reading a child's *index* has to
163    /// account for both.
164    #[must_use]
165    #[inline]
166    pub fn is_missing(&self) -> bool {
167        self.0.is_missing()
168    }
169
170    /// An id unique to this node within its tree, stable for the tree's
171    /// lifetime. Suitable as a map key for per-node walk state.
172    #[inline]
173    #[must_use]
174    pub fn id(&self) -> usize {
175        self.0.id()
176    }
177
178    /// The grammar's name for this node's kind (`"function_item"`,
179    /// `"identifier"`, …). Several distinct [`kind_id`](Self::kind_id)s
180    /// can share one name — see the aliasing note on `kind_id`.
181    #[inline]
182    #[must_use]
183    pub fn kind(&self) -> &'static str {
184        self.0.kind()
185    }
186
187    /// The numeric kind of this node in its grammar's symbol table.
188    ///
189    /// This is what the per-language token enums in [`languages`]
190    /// (`Rust::FunctionItem as u16`, …) and every classifier match on.
191    /// The value is an index into the pinned grammar and moves whenever
192    /// that grammar is bumped; it is not stable across versions.
193    ///
194    /// [`languages`]: crate::languages
195    #[inline]
196    #[must_use]
197    pub fn kind_id(&self) -> u16 {
198        self.0.kind_id()
199    }
200
201    /// The source text this node spans, or `None` when the span falls
202    /// outside `data` or those bytes are not valid UTF-8.
203    ///
204    /// Indexed with `data.get(..)` rather than delegating to
205    /// [`tree_sitter::Node::utf8_text`], which is
206    /// `str::from_utf8(&source[start..end])` — the slice panics on a span
207    /// reaching past `source`, before the `Result` its signature offers
208    /// ever exists, so `.ok()` cannot catch it. Every caller today passes
209    /// the same buffer the tree was parsed from and so cannot produce
210    /// that span, but the guarantee is one refactor from false: this
211    /// crate already runs passes that parse one buffer and read another
212    /// (`preproc`, `comment_rm`), and `panic!` is banned in non-test code
213    /// whether or not the branch is currently reachable. `data.get(..)`
214    /// is also what `.claude/rules/grammar-dispatch.md` §10 prescribes
215    /// for reading a node's bytes.
216    #[inline]
217    #[must_use]
218    pub fn utf8_text(&self, data: &'a [u8]) -> Option<&'a str> {
219        let bytes = data.get(self.0.start_byte()..self.0.end_byte())?;
220        std::str::from_utf8(bytes).ok()
221    }
222
223    /// The 0-based byte offset where this node starts.
224    #[inline]
225    #[must_use]
226    pub fn start_byte(&self) -> usize {
227        self.0.start_byte()
228    }
229
230    /// The 0-based byte offset one past this node's last byte.
231    #[inline]
232    #[must_use]
233    pub fn end_byte(&self) -> usize {
234        self.0.end_byte()
235    }
236
237    /// The 0-based `(row, column)` where this node starts.
238    #[inline]
239    #[must_use]
240    pub fn start_position(&self) -> (usize, usize) {
241        let temp = self.0.start_position();
242        (temp.row, temp.column)
243    }
244
245    /// The 0-based `(row, column)` one past this node's end.
246    #[inline]
247    #[must_use]
248    pub fn end_position(&self) -> (usize, usize) {
249        let temp = self.0.end_position();
250        (temp.row, temp.column)
251    }
252
253    /// The 0-based row this node starts on.
254    #[inline]
255    #[must_use]
256    pub fn start_row(&self) -> usize {
257        self.0.start_position().row
258    }
259
260    /// The 0-based row of this node's end position. A node ending at
261    /// column 0 does not occupy that row; see [`end_line`](Self::end_line).
262    #[inline]
263    #[must_use]
264    pub fn end_row(&self) -> usize {
265        self.0.end_position().row
266    }
267
268    /// The 1-based **last line this node occupies** — the inclusive end
269    /// of its line span.
270    ///
271    /// Converting tree-sitter's 0-based end row needs a `+ 1` only when
272    /// the node actually occupies the row it ends on. A node whose end
273    /// column is 0 finished at the *start* of the row below its last
274    /// content row, having absorbed the preceding newline, so that row
275    /// is not part of its span.
276    ///
277    /// Getting this wrong is invisible in almost every grammar, because
278    /// almost every function node ends just past its closing delimiter
279    /// and therefore at a column above 0. Perl's trailing `sub` does
280    /// not: it ends at column 0 of the row below its `}`, exactly where
281    /// the file root ends, and a blanket `+ 1` then reported it ending a
282    /// line past both its enclosing space and EOF (#1163) — the shape
283    /// behind the release `usize` underflow in #1051.
284    #[inline]
285    #[must_use]
286    pub fn end_line(&self) -> usize {
287        let end = self.0.end_position();
288        if end.column == 0 {
289            end.row
290        } else {
291            end.row + 1
292        }
293    }
294
295    /// Returns this node's parent.
296    ///
297    /// **`O(depth)`, not `O(1)`.** tree-sitter stores no parent pointer:
298    /// `ts_node_parent` restarts at the tree root and descends. A single
299    /// call in a per-node metric therefore costs `O(nodes × depth)` over a
300    /// walk, and an ancestor *loop* built on it costs `O(depth²)` per call.
301    ///
302    /// This has bitten the analyzer for real: #1052 was a per-leaf
303    /// `successors(node, Node::parent)` walk in `Tokens` that made the
304    /// metric `O(leaves × depth²)`, so a 2 KB file of nested parentheses
305    /// took ~19 s. Prefer inheriting state downward through the
306    /// traversal (see `Walk` in `spaces::compute`) over rediscovering it
307    /// upward, and where a predicate genuinely needs an ancestor, take
308    /// an [`Ancestors`] rather than calling this (#1084).
309    ///
310    /// As of #1096 no code the metric, `ops`, `bca function`, or
311    /// comment-removal walks reach calls this; the remaining callers are
312    /// `Ancestors` itself (the no-chain fallback), the one-off start node
313    /// of a `dump`, and tests. `rg '\.parent\(\)' src/` re-checks that.
314    #[inline]
315    pub fn parent(&self) -> Option<Node<'a>> {
316        self.0.parent().map(Node)
317    }
318
319    /// Returns `true` if this node's parent has any direct child with
320    /// the given grammar `kind_id` (the parent's children include this
321    /// node itself, so a self-match counts). Delegates to [`wraps_any`]
322    /// on the parent. This sits on the JS/TS arrow-function
323    /// closure-classification hot path (`check_if_arrow_func!`); see
324    /// #521.
325    ///
326    /// `ancestors` supplies the parent, for [`Node::parent`]'s
327    /// `O(depth)` reason (#1088).
328    ///
329    /// [`wraps_any`]: Self::wraps_any
330    #[inline]
331    #[must_use]
332    pub fn has_sibling(&self, ancestors: Ancestors<'a, '_>, id: u16) -> bool {
333        ancestors
334            .parent(self)
335            .is_some_and(|parent| parent.is_child(id))
336    }
337
338    /// The sibling immediately before this node.
339    ///
340    /// **`O(depth)`, not `O(1)`**, for [`Node::parent`]'s reason:
341    /// `ts_node__prev_sibling` opens with `ts_node_parent`. Callers on a
342    /// walk should use [`Ancestors::previous_sibling`] instead (#1096).
343    #[inline]
344    pub fn previous_sibling(&self) -> Option<Node<'a>> {
345        node_resolved_sibling_lookups::record();
346        self.0.prev_sibling().map(Node)
347    }
348
349    /// The sibling immediately after this node.
350    ///
351    /// **`O(depth)`, not `O(1)`**, for [`previous_sibling`]'s reason:
352    /// `ts_node_next_sibling` opens with `ts_node_parent`. There is no
353    /// [`Ancestors`] counterpart because the only caller wants exactly
354    /// this cost — the `exclude_tests` attribute lookahead reads forward
355    /// over the parent's children whenever the parent is narrow enough,
356    /// and falls back here precisely for the wide-and-shallow parent a
357    /// cursor pass would make `O(width)` per node (#1431). Anything on a
358    /// walk that does not budget the two against each other should be
359    /// reading the child list instead.
360    ///
361    /// [`previous_sibling`]: Self::previous_sibling
362    #[inline]
363    pub(crate) fn next_sibling(&self) -> Option<Node<'a>> {
364        node_resolved_sibling_lookups::record();
365        self.0.next_sibling().map(Node)
366    }
367
368    /// Returns `true` if any direct child has the given grammar
369    /// `kind_id`. See #217 for the motivating perf finding from the
370    /// JS/TS template-literal hot path.
371    #[inline]
372    #[must_use]
373    pub fn is_child(&self, id: u16) -> bool {
374        self.wraps_any(&[id])
375    }
376
377    /// Returns `true` if any direct child matches one of the given
378    /// grammar `kind_id`s. The single-id [`is_child`] delegates here, so
379    /// both share one child scan (the `#[inline]` makes the
380    /// single-element `contains` collapse to an equality check).
381    /// Generalizing the check to a set lets the shared
382    /// string-interpolation operand skip declare its rule once (issue
383    /// #420).
384    ///
385    /// # Why a cursor rather than `child(0)` + `next_sibling()`
386    ///
387    /// #217 replaced the cursor walk with a `next_sibling()` chain to
388    /// dodge the `TreeCursor` heap allocation, on the premise that a
389    /// sibling step is `O(1)`. It is not: `ts_node_next_sibling`
390    /// resolves the parent first, and `tree_sitter` stores no parent
391    /// pointer — it descends from the root — so each step cost
392    /// `O(depth)` and the scan `O(children × depth)`. That made every
393    /// caller of this method `O(depth)` per node, which is the same
394    /// defect #1084 removed from the predicates that ask for an
395    /// ancestor outright. The cursor iterator is `O(children)` after one
396    /// allocation, and measured faster on real input as well as on the
397    /// pathological one: the `nom/nested-arrow` probe went from
398    /// quadratic (17.6 s at depth 4000) to linear (6.3 ms), and a walk
399    /// over the 384-file `pdf.js` corpus dropped from ~443 ms to
400    /// ~370 ms (#1088).
401    ///
402    /// [`is_child`]: Self::is_child
403    #[inline]
404    #[must_use]
405    pub fn wraps_any(&self, ids: &[u16]) -> bool {
406        self.children().any(|c| ids.contains(&c.kind_id()))
407    }
408
409    /// The number of direct children, named and anonymous alike.
410    #[inline]
411    #[must_use]
412    pub fn child_count(&self) -> usize {
413        self.0.child_count()
414    }
415
416    /// Whether this node is a named grammar production, as opposed to an
417    /// anonymous token such as a punctuation or keyword literal. Walks
418    /// use it to skip tokens like the leading `|` in an or-pattern.
419    #[inline]
420    #[must_use]
421    pub fn is_named(&self) -> bool {
422        self.0.is_named()
423    }
424
425    /// Returns the direct child reached through the grammar `field_name`,
426    /// if any. The child carries the underlying tree lifetime `'a` (the
427    /// `tree_sitter::Node` it wraps is [`Copy`] and valid for the whole
428    /// tree), so callers may hold it past the borrow of `&self` — matching
429    /// the sibling accessors ([`child`], [`parent`], [`children`], …) rather
430    /// than over-narrowing to the method-call borrow (see issue #786).
431    ///
432    /// [`child`]: Self::child
433    /// [`parent`]: Self::parent
434    /// [`children`]: Self::children
435    #[inline]
436    pub fn child_by_field_name(&self, name: &str) -> Option<Node<'a>> {
437        self.0.child_by_field_name(name).map(Node)
438    }
439
440    /// The direct child at index `pos`, counting named and anonymous
441    /// children alike. Prefer [`child_by_field_name`](Self::child_by_field_name)
442    /// for any slot the grammar can precede with an optional preamble.
443    #[inline]
444    #[must_use]
445    pub fn child(&self, pos: usize) -> Option<Node<'a>> {
446        self.0.child(pos as u32).map(Node)
447    }
448
449    /// Returns the tree-sitter grammar field name through which this
450    /// node reaches the child at `child_index`, if any. Used by the
451    /// AST builder to thread the parent's `field_name` into each child
452    /// without a parallel cursor walk.
453    #[inline]
454    #[must_use]
455    pub fn field_name_for_child(&self, child_index: u32) -> Option<&'static str> {
456        self.0.field_name_for_child(child_index)
457    }
458
459    /// Iterator over this node's direct children.
460    ///
461    /// Builds a [`Cursor`], which heap-allocates. A loop that visits
462    /// many nodes should hoist one cursor and use [`children_with`]
463    /// instead; see its note for when the difference is worth the
464    /// plumbing.
465    ///
466    /// [`children_with`]: Self::children_with
467    #[inline]
468    #[must_use]
469    pub fn children(&self) -> Children<'a> {
470        child_scan_cursors::record();
471        // `descend`, not `seed`: `ts_node_walk` already ran the
472        // `ts_tree_cursor_init` that `Cursor::reset` would run again.
473        let mut cursor = self.cursor();
474        let scan = ChildScan::descend(self, &mut cursor);
475        Children { cursor, scan }
476    }
477
478    /// [`children`], over a cursor the caller owns.
479    ///
480    /// `tree_sitter::TreeCursor` heap-allocates its stack when built and
481    /// frees it when dropped, so a traversal that calls [`children`]
482    /// once per visited node pays a `malloc`/`free` pair per node.
483    /// `ts_tree_cursor_reset` keeps the allocation, so a loop that can
484    /// hoist one cursor pays for it once however many nodes it visits
485    /// (#1112).
486    ///
487    /// Worth the plumbing only where a loop visits many nodes. Measured
488    /// on the corpus slice, a full metric walk reaches [`children`] on
489    /// 3-6 % of nodes in C++, Rust, JavaScript, and Java, 16 % in C#,
490    /// and 60 % in Python, where one scan — the instance-attribute walk
491    /// in `metrics::npa::python` — was 92 % of the total, and
492    /// [`preorder`] plus the suppression DFS are the rest. Crate-wide
493    /// there is one more per-node consumer, `output::dump`'s renderer,
494    /// which no metric walk runs and so is absent from that total.
495    /// Predicates
496    /// that hold a bare `&Node` and scan one node's children keep
497    /// [`children`]: threading a cursor to them would cross the
498    /// `Checker` / `Getter` trait surface for one allocation per call.
499    ///
500    /// [`preorder`]: Self::preorder
501    ///
502    /// [`children`]: Self::children
503    #[inline]
504    pub fn children_with<'c>(&self, cursor: &'c mut Cursor<'a>) -> ChildrenWith<'c, 'a> {
505        let scan = ChildScan::seed(self, cursor);
506        ChildrenWith { cursor, scan }
507    }
508
509    /// The children after `child`, nearest first: what
510    /// `children().skip_while(|c| c.id() != child.id()).skip(1)` yields,
511    /// and nothing when `child` is not one of this node's children.
512    ///
513    /// That scan costs `child`'s index, so a walk that asks it of every
514    /// child of a wide list is quadratic in the list (#1545). This finds
515    /// `child` in `O(log n)` instead; see [`Node::children_before`].
516    pub fn children_after(&self, child: &Node<'a>) -> impl Iterator<Item = Node<'a>> + use<'a> {
517        let mut cursor = self.cursor();
518        let mut more =
519            self.seat_on_child(child, &mut cursor).is_some() && cursor.goto_next_sibling();
520        std::iter::from_fn(move || {
521            let next = more.then(|| cursor.node())?;
522            more = cursor.goto_next_sibling();
523            Some(next)
524        })
525    }
526
527    /// The children before `child`, nearest first, and nothing when
528    /// `child` is not one of this node's children.
529    ///
530    /// `child`'s index comes from [`Cursor::goto_first_child_for_byte`]
531    /// and each sibling from [`Node::child`], both `O(log n)` in the
532    /// child count, for the reason the first gives: a `repeat()` list is
533    /// stored as a balanced tree of hidden nodes.
534    pub fn children_before(&self, child: &Node<'a>) -> impl Iterator<Item = Node<'a>> + use<'a> {
535        let node = *self;
536        let index = self.seat_on_child(child, &mut self.cursor()).unwrap_or(0);
537        (0..index).rev().filter_map(move |i| node.child(i))
538    }
539
540    // Seats `cursor`, which sits on this node, on `child` and returns its
541    // index, or `None` when `child` is not one of this node's children.
542    fn seat_on_child(&self, child: &Node<'a>, cursor: &mut Cursor<'a>) -> Option<usize> {
543        if let Some(index) = cursor.goto_first_child_for_byte(child.start_byte())
544            && cursor.node().id() == child.id()
545        {
546            return Some(index);
547        }
548        // The lookup passes over a zero-width child, which ends at the
549        // byte it starts at; only error recovery inserts one.
550        cursor.reset(self);
551        let mut index = 0;
552        let mut found = cursor.goto_first_child();
553        while found && cursor.node().id() != child.id() {
554            found = cursor.goto_next_sibling();
555            index += 1;
556        }
557        found.then_some(index)
558    }
559
560    /// A fresh cursor positioned on this node, for the traversals that
561    /// reuse one cursor across many child scans
562    /// ([`children_with`](Self::children_with)).
563    #[inline]
564    #[must_use]
565    pub fn cursor(&self) -> Cursor<'a> {
566        Cursor(self.0.walk())
567    }
568
569    /// Counts this node's ancestors satisfying `check`, walking upward
570    /// from the parent and stopping at (and excluding) the first
571    /// ancestor satisfying `stop`. An ancestor that is the `if` of an
572    /// `else if` chain never counts — it is a continuation of the
573    /// branch above it, not a new enclosing one.
574    ///
575    /// `ancestors` is the chain the caller descended through. Passing
576    /// [`Ancestors::unknown`] is always correct and answers identically;
577    /// it just pays `O(depth)` per step instead of `O(1)`.
578    pub fn count_specific_ancestors<C: Checker>(
579        &self,
580        ancestors: Ancestors<'a, '_>,
581        check: fn(&Node) -> bool,
582        stop: fn(&Node) -> bool,
583    ) -> usize {
584        let mut count = 0;
585        for (parent, above_parent) in ancestors.iter(self) {
586            if stop(&parent) {
587                break;
588            }
589            if check(&parent) && !C::is_else_if(&parent, above_parent) {
590                count += 1;
591            }
592        }
593        count
594    }
595
596    /// Returns `true` iff this node's parent satisfies `parent_pred`
597    /// AND that parent's own parent (this node's grandparent)
598    /// satisfies `grand_pred`. Returns `false` as soon as either link
599    /// is absent or its predicate fails, so a misordered predicate
600    /// cannot silently degrade to a single-predicate check.
601    ///
602    /// `ancestors` is the chain the caller descended through. Passing
603    /// [`Ancestors::unknown`] is always correct and answers
604    /// identically; it just pays [`Node::parent`]'s `O(depth)` for each
605    /// of the two links, which on a per-node metric arm is quadratic in
606    /// nesting depth (#1096).
607    pub fn parent_grandparent_match(
608        &self,
609        ancestors: Ancestors<'a, '_>,
610        parent_pred: fn(&Node) -> bool,
611        grand_pred: fn(&Node) -> bool,
612    ) -> bool {
613        let mut climb = ancestors.iter(self);
614        let Some((parent, _)) = climb.next() else {
615            return false;
616        };
617        if !parent_pred(&parent) {
618            return false;
619        }
620        let Some((grand, _)) = climb.next() else {
621            return false;
622        };
623        grand_pred(&grand)
624    }
625
626    /// Returns a pre-order iterator over this node and all of its
627    /// descendants (this node first, then each child subtree left to
628    /// right).
629    ///
630    /// The traversal is allocation-light: it reuses one work stack *and
631    /// one cursor*, and visits each node exactly once, so a full walk is
632    /// O(n) in the subtree size and allocates only the stack's growth.
633    /// Building a fresh cursor per visited node instead would cost a
634    /// `malloc`/`free` pair per node (#1112). Every yielded [`Node`]
635    /// carries the underlying tree lifetime `'a`, so callers may collect
636    /// or retain the handles.
637    ///
638    /// This is the Rust counterpart of the Python `Node.walk()` binding
639    /// (issue #728): the binding wraps each yielded node, so Rust and
640    /// Python share one traversal order.
641    #[must_use]
642    pub fn preorder(&self) -> Preorder<'a> {
643        Preorder {
644            stack: vec![*self],
645            cursor: self.cursor(),
646        }
647    }
648
649    /// Collects every node in this subtree (this node included) whose
650    /// [`kind`](tree_sitter::Node::kind) is listed in `kinds`, in
651    /// pre-order.
652    ///
653    /// Membership is an exact match against the raw grammar kind — the
654    /// same unaltered vocabulary `big_code_analysis::Ast::root_node` exposes, not
655    /// the `Alterator`-curated kinds `big_code_analysis::Ast::dump` emits. This is
656    /// the Rust counterpart of the Python `Node.descendants_by_kind()`
657    /// binding (issue #728).
658    #[must_use]
659    pub fn descendants_by_kind(&self, kinds: &[&str]) -> Vec<Node<'a>> {
660        self.preorder()
661            .filter(|node| kinds.contains(&node.kind()))
662            .collect()
663    }
664}
665
666/// The chain of a node's ancestors, root first, as recorded by a walker
667/// that descended to that node.
668///
669/// A predicate that asks a node for its parent pays [`Node::parent`]'s
670/// `O(depth)` once per node, so `O(depth²)` over a deeply nested file,
671/// however few parent steps it takes (#1084). A walker that visits
672/// parents before children already holds the chain, and handing it down
673/// turns each step into a slice index — the upward counterpart of the
674/// downward flag propagation #1052 and #1062 used.
675///
676/// Callers that reached a node some other way pass
677/// [`Ancestors::unknown`], which climbs with [`Node::parent`]: the same
678/// answers at the original cost.
679#[derive(Clone, Copy, Debug)]
680pub struct Ancestors<'tree, 'chain>(Option<&'chain [Node<'tree>]>);
681
682impl<'tree, 'chain> Ancestors<'tree, 'chain> {
683    /// No chain is available; every query climbs with [`Node::parent`].
684    #[inline]
685    #[must_use]
686    pub const fn unknown() -> Self {
687        Self(None)
688    }
689
690    /// `chain` lists every ancestor of the node about to be queried,
691    /// root first — so `chain.last()` is its parent and an empty chain
692    /// means the node is the root.
693    #[inline]
694    #[must_use]
695    pub const fn known(chain: &'chain [Node<'tree>]) -> Self {
696        Self(Some(chain))
697    }
698
699    /// [`Ancestors::known`], but first checks that `chain` really is
700    /// `node`'s ancestry.
701    ///
702    /// The parity test below proves the walker's truncate/push rule on a
703    /// *replica* walker, so it cannot see `metrics_inner` itself
704    /// desynchronising — a `chain.push` moved ahead of the per-node
705    /// computes, say, or a `continue` inserted above the truncate.
706    /// [`Ancestors::parent`] trusts `chain.last()` unvalidated, so such a
707    /// drift would feed every predicate a wrong ancestor silently rather
708    /// than fail. Walkers that maintain a chain should construct through
709    /// here; [`Ancestors::known`] stays unchecked for the callers that
710    /// deliberately pair a chain with a foreign node.
711    ///
712    /// # Two checks, because the exact one is not affordable by default
713    ///
714    /// The invariant is "`chain.last()` **is** `node.parent()`", and
715    /// asking that outright costs [`Node::parent`]'s `O(depth)` — the
716    /// very lookup #1084 exists to remove. Per node, on every walk that
717    /// constructs a checked chain, it made every debug-build walk
718    /// `O(nodes × depth)` while the shipped walk is `O(nodes)`: a tax on
719    /// every `cargo test`, worst on the deep-nesting regression tests
720    /// that exist to pin the shipped walk's linearity (#1122). It now
721    /// runs only under `--cfg chain_audit`, which `make chain-audit` and
722    /// the CI lane of the same name set — and there as a plain
723    /// `assert_eq!`, so the audit has teeth in a release profile too.
724    ///
725    /// What stays on in every debug build is an `O(1)` *consequence* of
726    /// that invariant: a parent's byte span contains its child's, and no
727    /// node is its own parent. Strictly weaker — a grandparent contains
728    /// the node as well — but it is shaped to the two ways a walker
729    /// desynchronises. A `push` moved ahead of the per-node computes
730    /// leaves `chain.last() == node`; a dropped `truncate` leaves the
731    /// previous subtree's path, whose last entry is disjoint from the
732    /// node that follows it. Both trip here, on the first node that
733    /// shows them, at four integer comparisons.
734    #[must_use]
735    pub fn checked(chain: &'chain [Node<'tree>], node: &Node<'tree>) -> Self {
736        // Opt-in only: `Node::parent` restarts at the root, so this is
737        // the `O(nodes × depth)` walk described above.
738        #[cfg(chain_audit)]
739        assert_eq!(
740            chain.last().map(Node::id),
741            node.parent().map(|parent| parent.id()),
742            "ancestor chain desynchronised on a {} node",
743            node.kind()
744        );
745        debug_assert!(
746            chain.last().is_none_or(|parent| {
747                parent.id() != node.id()
748                    && parent.start_byte() <= node.start_byte()
749                    && node.end_byte() <= parent.end_byte()
750            }),
751            "ancestor chain desynchronised on a {} node: chain.last() neither \
752             contains it nor differs from it",
753            node.kind()
754        );
755        Self::known(chain)
756    }
757
758    /// How far the node this chain describes sits from the root, or
759    /// `None` when no chain is known — deriving it then would cost the
760    /// [`Node::parent`] climb the chain exists to remove.
761    #[inline]
762    pub fn depth(self) -> Option<usize> {
763        self.0.map(<[Node<'tree>]>::len)
764    }
765
766    /// `node`'s parent.
767    #[inline]
768    #[must_use]
769    pub fn parent(self, node: &Node<'tree>) -> Option<Node<'tree>> {
770        match self.0 {
771            Some(chain) => chain.last().copied(),
772            None => node.parent(),
773        }
774    }
775
776    /// Whether `node`'s parent has kind id `kind`.
777    ///
778    /// The compound-leaf guard of `.claude/rules/grammar-dispatch.md`
779    /// section 5 asks this and nothing else: a delimiter or keyword
780    /// token is suppressed *only* directly under the construct that
781    /// owns it, never under an arbitrary ancestor. Eleven dispatch
782    /// arms across `getter`, `checker` and the metric walkers spelled
783    /// it out as `.parent(node).is_some_and(|p| p.kind_id() == X as
784    /// u16)`, which wraps onto four rustfmt lines inside a match guard
785    /// and buries the question under the plumbing; #1314 added six more
786    /// and folded all seventeen onto this. Three sites spelling their
787    /// binding `|parent|` were left alone and would each fit:
788    /// `impl_is_else_if_parent_clause!` (`src/checker.rs`),
789    /// `is_stabby_lambda_body` (`src/checker/ruby.rs`) and
790    /// `is_useful_comment` (`src/checker/rust.rs`, a let-chain).
791    ///
792    /// A `false` when `node` has no parent is the answer every one of
793    /// those call sites wants: a root node's token is not inside the
794    /// construct, so it is not suppressed.
795    #[inline]
796    #[must_use]
797    pub fn parent_has_kind(self, node: &Node<'tree>, kind: u16) -> bool {
798        self.parent(node).is_some_and(|p| p.kind_id() == kind)
799    }
800
801    /// The sibling immediately before `node`, or `None` when `node` is
802    /// its parent's first child or has no parent.
803    ///
804    /// `tree_sitter`'s own `prev_sibling` resolves the parent first
805    /// (`ts_node__prev_sibling` opens with `ts_node_parent`), so it
806    /// carries the same `O(depth)` cost [`Node::parent`] does. With a
807    /// known chain the parent is free and what remains is a cursor walk
808    /// over the siblings.
809    #[inline]
810    #[must_use]
811    pub fn previous_sibling(self, node: &Node<'tree>) -> Option<Node<'tree>> {
812        let Some(chain) = self.0 else {
813            return node.previous_sibling();
814        };
815        let parent = chain.last()?;
816        let mut previous = None;
817        for child in parent.children() {
818            if child.id() == node.id() {
819                return previous;
820            }
821            previous = Some(child);
822        }
823        // `node` is not among `parent.children()`, so this chain does
824        // not describe `node`. Answering `None` would claim "no
825        // previous sibling", which is a different (and wrong) answer;
826        // fall back to the authoritative lookup instead.
827        node.previous_sibling()
828    }
829
830    /// `node`'s ancestors, nearest first, each paired with *its* own
831    /// ancestry so a predicate applied to an ancestor stays as cheap as
832    /// one applied to `node`.
833    #[inline]
834    #[must_use]
835    pub fn iter(self, node: &Node<'tree>) -> AncestorIter<'tree, 'chain> {
836        match self.0 {
837            Some(chain) => AncestorIter::Chain(chain),
838            None => AncestorIter::Climb(node.parent()),
839        }
840    }
841}
842
843/// Ancestor iterator returned by [`Ancestors::iter`], nearest first.
844pub enum AncestorIter<'tree, 'chain> {
845    /// The not-yet-yielded prefix of a known chain. Its last element is
846    /// the next ancestor, and the prefix before it is that ancestor's
847    /// own chain — so splitting from the back hands out both at once.
848    Chain(&'chain [Node<'tree>]),
849    /// The next ancestor to yield, reached by climbing.
850    Climb(Option<Node<'tree>>),
851}
852
853impl<'tree, 'chain> Iterator for AncestorIter<'tree, 'chain> {
854    type Item = (Node<'tree>, Ancestors<'tree, 'chain>);
855
856    fn next(&mut self) -> Option<Self::Item> {
857        match self {
858            Self::Chain(remaining) => {
859                let (&nearest, above) = remaining.split_last()?;
860                *remaining = above;
861                Some((nearest, Ancestors::known(above)))
862            }
863            Self::Climb(next) => {
864                let nearest = (*next)?;
865                *next = nearest.parent();
866                Some((nearest, Ancestors::unknown()))
867            }
868        }
869    }
870}
871
872/// Pre-order iterator over a node and its descendants, returned by
873/// [`Node::preorder`].
874///
875/// Holds a single work stack of not-yet-visited nodes, and a single
876/// cursor to enumerate each node's children with. Each step pops the
877/// next node, pushes its children so the leftmost is visited first, and
878/// yields the popped node — so the sequence is the node, then each child
879/// subtree in order. Both are reused across steps (children are pushed
880/// then the freshly-pushed slice is reversed in place), so the walk
881/// allocates only the stack's growth: no fresh buffer per node, and no
882/// fresh `TreeCursor` either (#1112).
883pub struct Preorder<'a> {
884    stack: Vec<Node<'a>>,
885    cursor: Cursor<'a>,
886}
887
888impl<'a> Iterator for Preorder<'a> {
889    type Item = Node<'a>;
890
891    fn next(&mut self) -> Option<Self::Item> {
892        let node = self.stack.pop()?;
893        // Push children in document order, then reverse just the slice we
894        // appended so the leftmost child ends up on top of the stack and
895        // is visited next — pre-order without a per-node temporary.
896        let first_child = self.stack.len();
897        // Destructured so the stack and the cursor are borrowed as the
898        // disjoint fields they are.
899        let Self { stack, cursor } = self;
900        stack.extend(node.children_with(cursor));
901        stack[first_child..].reverse();
902        Some(node)
903    }
904}
905
906/// An `AST` cursor.
907#[derive(Clone)]
908pub struct Cursor<'a>(TreeCursor<'a>);
909
910impl<'a> Cursor<'a> {
911    /// Re-seats the cursor on `node`.
912    #[inline]
913    pub fn reset(&mut self, node: &Node<'a>) {
914        self.0.reset(node.0);
915    }
916
917    /// Moves to the next sibling; `false` when there is none.
918    #[inline]
919    pub fn goto_next_sibling(&mut self) -> bool {
920        self.0.goto_next_sibling()
921    }
922
923    /// Moves to the first child; `false` when there is none.
924    #[inline]
925    pub fn goto_first_child(&mut self) -> bool {
926        self.0.goto_first_child()
927    }
928
929    /// Moves to the first child that ends after `byte` and returns its
930    /// index in [`Node::children`] order, or `None` when no child does.
931    ///
932    /// "Ends after" is strict, so a **zero-width** child at `byte` — a
933    /// MISSING token the parser inserted during error recovery — is
934    /// skipped and the cursor lands on its successor. That is the one way
935    /// this differs from `children().position(..)`, which finds such a
936    /// node; a caller resolving a node's own index must therefore check
937    /// [`Cursor::node`] against it, as both call sites here do.
938    ///
939    /// The index is what makes this worth having over a sibling scan:
940    /// tree-sitter stores a `repeat()` child list under balanced hidden
941    /// nodes and skips each by its cached visible-child count, so this
942    /// is `O(log n)` in the sibling count where `children().position(..)`
943    /// is `O(n)` — the difference between a linear and a quadratic walk
944    /// when every child of a wide list asks for its own index.
945    #[inline]
946    pub fn goto_first_child_for_byte(&mut self, byte: usize) -> Option<usize> {
947        self.0.goto_first_child_for_byte(byte)
948    }
949
950    /// The node the cursor currently sits on.
951    #[inline]
952    #[must_use]
953    pub fn node(&self) -> Node<'a> {
954        Node(self.0.node())
955    }
956}
957
958/// Position of a child scan, independent of who owns the cursor driving
959/// it.
960///
961/// [`Children`] and [`ChildrenWith`] differ only in that — one owns its
962/// cursor, the other borrows the caller's — and `children_with` exists
963/// to save an allocation, not to answer differently. Keeping the
964/// termination rule here rather than in each iterator is what stops the
965/// two from drifting.
966struct ChildScan {
967    done: bool,
968    remaining: usize,
969}
970
971impl ChildScan {
972    /// Seats `cursor` on `node`'s first child.
973    ///
974    /// `goto_first_child` returns false when the node has no children,
975    /// in which case the scan is exhausted from the outset. Termination
976    /// is then driven entirely by the cursor (see [`ChildScan::step`]),
977    /// so the iterator stops exactly when the tree reports no further
978    /// siblings — it can never pad the sequence with duplicate nodes if
979    /// `child_count` and the cursor walk ever disagree.
980    ///
981    /// `child_count` is the authoritative length for the
982    /// `ExactSizeIterator` contract; for well-formed trees it equals the
983    /// cursor sibling walk, so the reported length and the emitted data
984    /// agree. A childless node reports `0` so the empty iterator's
985    /// length matches its (lack of) data.
986    fn seed<'a>(node: &Node<'a>, cursor: &mut Cursor<'a>) -> Self {
987        cursor.reset(node);
988        Self::descend(node, cursor)
989    }
990
991    /// [`ChildScan::seed`], for a cursor already seated on `node` —
992    /// which is what [`Node::cursor`] hands back, and `ts_node_walk` and
993    /// `ts_tree_cursor_reset` run the same `ts_tree_cursor_init`. Only
994    /// [`Node::children`] may skip the reset; every other caller reuses
995    /// a cursor left wherever the previous scan ended.
996    fn descend<'a>(node: &Node<'a>, cursor: &mut Cursor<'a>) -> Self {
997        let done = !cursor.goto_first_child();
998        Self {
999            done,
1000            remaining: if done { 0 } else { node.child_count() },
1001        }
1002    }
1003
1004    /// Yields the cursor's current child and advances past it.
1005    fn step<'a>(&mut self, cursor: &mut Cursor<'a>) -> Option<Node<'a>> {
1006        if self.done {
1007            return None;
1008        }
1009        let result = cursor.node();
1010        // The cursor is the single source of truth for termination:
1011        // once there is no next sibling this yield is the last one.
1012        self.done = !cursor.goto_next_sibling();
1013        // Keep the advertised length consistent with termination: when
1014        // the cursor stops, nothing remains. For well-formed trees this
1015        // equals `child_count - emitted`; if the cursor walk and
1016        // `child_count` ever disagree, this still honors the
1017        // `ExactSizeIterator` contract (`len() == 0` exactly at
1018        // exhaustion) rather than reporting a phantom remainder.
1019        self.remaining = if self.done {
1020            0
1021        } else {
1022            self.remaining.saturating_sub(1)
1023        };
1024        Some(result)
1025    }
1026
1027    fn size_hint(&self) -> (usize, Option<usize>) {
1028        (self.remaining, Some(self.remaining))
1029    }
1030}
1031
1032/// Iterator over a node's direct children, returned by
1033/// [`Node::children`]. Owns the cursor it walks with.
1034///
1035/// Termination is driven by the cursor alone: each step yields the
1036/// cursor's current node, then advances with `goto_next_sibling`,
1037/// stopping the moment that returns false. This makes the cursor the
1038/// single source of truth for both the emitted data and when to stop, so
1039/// the sequence can never be padded with duplicates if `child_count` and
1040/// the actual sibling walk disagree.
1041///
1042/// The `ExactSizeIterator` length is reported from `child_count` (tracked
1043/// in `ChildScan`). For well-formed trees the cursor walk and
1044/// `child_count` agree, so the advertised length matches the data.
1045pub struct Children<'a> {
1046    cursor: Cursor<'a>,
1047    scan: ChildScan,
1048}
1049
1050impl<'a> Iterator for Children<'a> {
1051    type Item = Node<'a>;
1052
1053    fn next(&mut self) -> Option<Self::Item> {
1054        self.scan.step(&mut self.cursor)
1055    }
1056
1057    fn size_hint(&self) -> (usize, Option<usize>) {
1058        self.scan.size_hint()
1059    }
1060}
1061
1062impl ExactSizeIterator for Children<'_> {}
1063
1064/// Iterator over a node's direct children, returned by
1065/// [`Node::children_with`]. Borrows the caller's cursor rather than
1066/// building one, which is the whole of the difference: it yields exactly
1067/// what [`Children`] yields, through the same `ChildScan`.
1068pub struct ChildrenWith<'c, 'a> {
1069    cursor: &'c mut Cursor<'a>,
1070    scan: ChildScan,
1071}
1072
1073impl<'a> Iterator for ChildrenWith<'_, 'a> {
1074    type Item = Node<'a>;
1075
1076    fn next(&mut self) -> Option<Self::Item> {
1077        self.scan.step(self.cursor)
1078    }
1079
1080    fn size_hint(&self) -> (usize, Option<usize>) {
1081        self.scan.size_hint()
1082    }
1083}
1084
1085impl ExactSizeIterator for ChildrenWith<'_, '_> {}
1086
1087impl<'a> Search<'a> for Node<'a> {
1088    fn act_on_node(&self, action: &mut dyn FnMut(&Node<'a>, Ancestors<'a, '_>)) {
1089        let mut cursor = self.cursor();
1090        let mut stack = Vec::new();
1091        // Ancestor chain of the node being visited, root first. Kept by
1092        // the same truncate/push rule as the metric walk, so a predicate
1093        // the action applies can read an ancestor as a slice index
1094        // rather than through the `O(depth)` `Node::parent` (#1088).
1095        //
1096        // Seeded with this subtree root's own ancestry rather than left
1097        // empty: `Ancestors` reads an empty chain as "this node is the
1098        // tree root", so on a subtree an empty seed would report no
1099        // parent for `*self` — silently costing e.g. the JS getters the
1100        // binding a `function_expression` takes its name from. One
1101        // climb, and none at all for the tree root this is called on
1102        // today.
1103        let mut chain: Vec<Node<'a>> = std::iter::successors(self.parent(), Node::parent).collect();
1104        chain.reverse();
1105        let depth = chain.len();
1106
1107        stack.push((*self, depth));
1108
1109        while let Some((node, depth)) = stack.pop() {
1110            chain.truncate(depth);
1111            action(&node, Ancestors::checked(&chain, &node));
1112            chain.push(node);
1113            // Source order in, tail reversed in place, so the LIFO
1114            // `stack` yields the leftmost child first — pre-order with
1115            // no staging buffer.
1116            let first_child = stack.len();
1117            stack.extend(
1118                node.children_with(&mut cursor)
1119                    .map(|child| (child, depth + 1)),
1120            );
1121            stack[first_child..].reverse();
1122        }
1123    }
1124
1125    fn first_child(&self, pred: fn(u16) -> bool) -> Option<Node<'a>> {
1126        self.children().find(|&child| pred(child.kind_id()))
1127    }
1128
1129    fn act_on_child(&self, action: &mut dyn FnMut(&Node<'a>)) {
1130        for child in self.children() {
1131            action(&child);
1132        }
1133    }
1134}
1135
1136#[cfg(test)]
1137mod tests {
1138    use super::*;
1139    use crate::langs::MozjsCode;
1140    use crate::test_support::for_each_node_with_chain;
1141
1142    /// The `child(0)` + `next_sibling()` chain [`Node::wraps_any`] used
1143    /// between #217 and #1088, kept here as the reference the cursor
1144    /// walk that replaced it is checked against.
1145    ///
1146    /// The swap was made for cost, not for behaviour: a sibling step
1147    /// resolves its parent, and `tree_sitter` resolves a parent by
1148    /// descending from the root, so the chain was `O(children × depth)`
1149    /// where the cursor is `O(children)`. Nothing about the *set* of
1150    /// children was supposed to change, and this is what says so —
1151    /// node-by-node over a real tree, same order and same short-circuit,
1152    /// without hardcoding grammar `kind_id`s.
1153    #[cfg(feature = "mozjs")]
1154    fn sibling_chain_has_sibling(node: OtherNode, id: u16) -> bool {
1155        node.parent().is_some_and(|parent| {
1156            let mut cur = parent.child(0);
1157            while let Some(c) = cur {
1158                if c.kind_id() == id {
1159                    return true;
1160                }
1161                cur = c.next_sibling();
1162            }
1163            false
1164        })
1165    }
1166
1167    /// `utf8_text` answers `None` for a span outside the buffer instead
1168    /// of aborting the process.
1169    ///
1170    /// The only way to reach that branch is to read against a buffer
1171    /// other than the one the tree was parsed from, which no caller does
1172    /// today — so this is the whole coverage the guard can have, and it
1173    /// is a real one: against the delegating implementation
1174    /// (`self.0.utf8_text(data).ok()`) this test panics at
1175    /// `str::from_utf8(&source[start..end])` rather than failing, which
1176    /// is precisely the behaviour the guard removes. Verified by revert.
1177    #[test]
1178    #[cfg(feature = "rust")]
1179    fn utf8_text_is_none_for_a_span_past_the_buffer() {
1180        let code = b"fn f() {}\n";
1181        let tree = Tree::new::<crate::langs::RustCode>(code);
1182        let root = tree.get_root();
1183
1184        // Sanity: the span is in range against its own buffer, so a
1185        // `None` below means the bound fired and not that the node is
1186        // empty or the text is not UTF-8.
1187        assert_eq!(root.utf8_text(code), Some("fn f() {}\n"));
1188
1189        // The same node, read against a buffer that ends mid-span.
1190        assert_eq!(root.utf8_text(&code[..4]), None);
1191    }
1192
1193    #[cfg(feature = "mozjs")]
1194    #[test]
1195    fn has_sibling_matches_the_retired_sibling_chain() {
1196        // Arrow functions exercise the `check_if_arrow_func!` call site
1197        // that motivated #521 (PropertyIdentifier siblings on the JS/TS
1198        // closure-classification hot path).
1199        let code = b"const o = { m: (a) => a + 1, n: function () {} }; foo.bar();";
1200        let tree = Tree::new::<MozjsCode>(code);
1201        let ts_tree = tree.as_ts_tree();
1202
1203        // Collect the grammar kinds that actually occur, so the
1204        // equivalence check covers present-sibling (true) cases.
1205        let mut kinds = std::collections::BTreeSet::new();
1206        let mut stack = vec![ts_tree.root_node()];
1207        while let Some(n) = stack.pop() {
1208            kinds.insert(n.kind_id());
1209            let mut child = n.child(0);
1210            while let Some(c) = child {
1211                stack.push(c);
1212                child = c.next_sibling();
1213            }
1214        }
1215        // Include an id that does not occur anywhere for absent-sibling
1216        // (false) coverage.
1217        let absent_id = u16::MAX;
1218
1219        let mut stack = vec![ts_tree.root_node()];
1220        let mut matched = 0;
1221        while let Some(n) = stack.pop() {
1222            let wrapped = Node(n);
1223            for &id in kinds.iter().chain(std::iter::once(&absent_id)) {
1224                let found = wrapped.has_sibling(Ancestors::unknown(), id);
1225                assert_eq!(
1226                    found,
1227                    sibling_chain_has_sibling(n, id),
1228                    "has_sibling diverged from the retired sibling chain at node kind {} for id {id}",
1229                    n.kind(),
1230                );
1231                matched += usize::from(found);
1232            }
1233            let mut child = n.child(0);
1234            while let Some(c) = child {
1235                stack.push(c);
1236                child = c.next_sibling();
1237            }
1238        }
1239        // The comment above claims the collected kinds cover the
1240        // present-sibling case; this enforces it. Both sides answering
1241        // `false` everywhere would agree without either scan ever
1242        // running to a match.
1243        assert!(
1244            matched > 0,
1245            "every answer was `false`, so the sibling scan was never exercised"
1246        );
1247
1248        // No-parent node (root) always reports no sibling.
1249        let root = Node(ts_tree.root_node());
1250        assert!(!root.has_sibling(Ancestors::unknown(), absent_id));
1251        for &id in &kinds {
1252            assert!(
1253                !root.has_sibling(Ancestors::unknown(), id),
1254                "root node has no parent → no sibling"
1255            );
1256        }
1257    }
1258
1259    /// `children()` must yield exactly the node's direct children, in
1260    /// order, for every node in a real tree — including the empty
1261    /// (leaf) and single-child cases. Termination is cursor-driven, so
1262    /// the emitted set is compared node-by-node against the raw
1263    /// tree-sitter `child(i)` walk (the ground truth for both order and
1264    /// count). This pins the no-duplicate-padding property: a desync
1265    /// between `child_count` and the cursor walk would surface here as
1266    /// extra trailing duplicates or a length mismatch.
1267    #[cfg(feature = "mozjs")]
1268    #[test]
1269    fn children_matches_tree_sitter_child_walk() {
1270        // Mix of leaf nodes (no children), single-child wrappers, and
1271        // multi-child constructs to cover all arities.
1272        let code = b"const o = { m: (a) => a + 1 }; foo(); ;";
1273        let tree = Tree::new::<MozjsCode>(code);
1274        let ts_tree = tree.as_ts_tree();
1275
1276        let mut stack = vec![ts_tree.root_node()];
1277        while let Some(n) = stack.pop() {
1278            let wrapped = Node(n);
1279
1280            // Ground truth: walk children by index off the raw node.
1281            let expected: Vec<_> = (0..n.child_count() as u32)
1282                .filter_map(|i| n.child(i))
1283                .map(|c| (c.id(), c.kind_id()))
1284                .collect();
1285
1286            let actual =
1287                drain_checking_exact_size(wrapped.children(), expected.len(), "children", n.kind());
1288            assert_eq!(
1289                actual,
1290                expected,
1291                "children() diverged from child(i) walk at kind {}",
1292                n.kind(),
1293            );
1294
1295            for i in 0..n.child_count() as u32 {
1296                if let Some(c) = n.child(i) {
1297                    stack.push(c);
1298                }
1299            }
1300        }
1301    }
1302
1303    /// `child_by_field_name` (issue #786) must return the child at the
1304    /// underlying tree lifetime `'a`, not the method-call borrow of
1305    /// `&self`. The proof is a helper whose return type *requires* the
1306    /// child to outlive an intermediate `&Node` borrow: under the old
1307    /// `Option<Node<'_>>` signature the returned node would be tied to
1308    /// `parent`'s borrow and this would fail to compile. Binding the
1309    /// child to a variable that outlives the `&parent` reborrow inside
1310    /// the helper exercises the widened lifetime.
1311    #[cfg(feature = "cpp")]
1312    #[test]
1313    fn child_by_field_name_outlives_self_borrow() {
1314        // `find_named_child` takes the parent by value, reborrows it
1315        // through a `&` reference to call `child_by_field_name`, and
1316        // returns the child. The returned `Node<'a>` must survive past
1317        // that inner `&parent` borrow — only possible because the child
1318        // carries the tree lifetime, not the borrow of `&parent`.
1319        fn find_named_child<'a>(parent: Node<'a>) -> Option<Node<'a>> {
1320            let borrowed: &Node<'a> = &parent;
1321            borrowed.child_by_field_name("declarator")
1322        }
1323
1324        let code = b"int answer = 42;";
1325        let tree = Tree::new::<crate::langs::CppCode>(code);
1326        let root = tree.get_root();
1327
1328        // Walk to the `declaration` node, then pull its `declarator`
1329        // child out and hold it after the producing borrow has ended.
1330        let mut held: Option<Node> = None;
1331        let mut stack = vec![root];
1332        while let Some(n) = stack.pop() {
1333            if n.kind() == "declaration" {
1334                // `find_named_child` consumes a copy of `n`; the result
1335                // must remain valid here, well past the inner borrow.
1336                held = find_named_child(n);
1337                break;
1338            }
1339            for child in n.children() {
1340                stack.push(child);
1341            }
1342        }
1343
1344        let declarator = held.expect("C declaration has a `declarator` field");
1345        // The held node is still usable: it kept its tree linkage rather
1346        // than dangling at the end of the producing borrow.
1347        assert_eq!(declarator.kind(), "init_declarator");
1348    }
1349
1350    /// `Node::as_tree_sitter` (issue #556) must hand back the *same*
1351    /// underlying `tree_sitter::Node` the wrapper holds: identical
1352    /// `kind()` / `kind_id()` and a usable tree-sitter API. Obtaining
1353    /// the wrapper through the public `CppParser` + `ParserTrait::root`
1354    /// path (rather than the in-module `Tree::new`) proves the accessor
1355    /// is the public seam that replaced the former `pub` `.0` field.
1356    #[cfg(feature = "cpp")]
1357    #[test]
1358    fn as_tree_sitter_round_trips_wrapper_kind() {
1359        use crate::{CppParser, ParserTrait};
1360        use std::path::Path;
1361
1362        let source = b"int main() { return 0; }";
1363        let parser = CppParser::new(source.to_vec(), Path::new("example.cpp"), None);
1364        let root = parser.root();
1365
1366        let ts_root = root.as_tree_sitter();
1367
1368        // A well-formed C++ translation unit roots at `translation_unit`.
1369        assert_eq!(ts_root.kind(), "translation_unit");
1370        // The accessor must agree with the wrapper's own kind views.
1371        assert_eq!(ts_root.kind(), root.kind());
1372        assert_eq!(ts_root.kind_id(), root.kind_id());
1373        // The returned node is usable as a tree-sitter node, not a copy
1374        // that has lost its tree linkage: the parse is error-free and
1375        // the root has children.
1376        assert!(!ts_root.has_error());
1377        assert!(ts_root.child_count() > 0);
1378    }
1379
1380    /// Ground-truth pre-order walk over the raw tree-sitter node, by
1381    /// document order (`child(0..child_count)`). [`Node::preorder`] must
1382    /// emit exactly this sequence of node ids — node first, then each
1383    /// child subtree left to right.
1384    #[cfg(feature = "cpp")]
1385    fn ground_truth_preorder(node: OtherNode) -> Vec<usize> {
1386        let mut out = vec![node.id()];
1387        for i in 0..node.child_count() as u32 {
1388            if let Some(child) = node.child(i) {
1389                out.extend(ground_truth_preorder(child));
1390            }
1391        }
1392        out
1393    }
1394
1395    #[cfg(feature = "cpp")]
1396    #[test]
1397    fn preorder_matches_recursive_document_order() {
1398        // A nested construct (function holding a declaration and a call)
1399        // gives the walk real depth and sibling fan-out to order.
1400        let code = b"int main() { int x = 1; foo(x); return 0; }";
1401        let tree = Tree::new::<crate::langs::CppCode>(code);
1402        let root = tree.get_root();
1403
1404        let actual: Vec<usize> = root.preorder().map(|n| n.id()).collect();
1405        let expected = ground_truth_preorder(root.as_tree_sitter());
1406
1407        assert_eq!(
1408            actual, expected,
1409            "preorder diverged from recursive child(0..n) document order"
1410        );
1411        // Sanity: a non-trivial tree, and the root is visited first.
1412        assert!(actual.len() > 5, "expected a multi-node tree");
1413        assert_eq!(actual[0], root.id(), "root must be yielded first");
1414    }
1415
1416    #[cfg(feature = "cpp")]
1417    #[test]
1418    fn descendants_by_kind_collects_matching_subtree_nodes() {
1419        // `x` is declared once and used twice, so three `identifier`
1420        // nodes exist under the function; `main` is an identifier too.
1421        let code = b"int main() { int x = 1; return x + x; }";
1422        let tree = Tree::new::<crate::langs::CppCode>(code);
1423        let root = tree.get_root();
1424
1425        let found = root.descendants_by_kind(&["identifier"]);
1426        // Cross-check against an independent pre-order count so the helper
1427        // cannot pass by matching everything or nothing.
1428        let expected: Vec<usize> = root
1429            .preorder()
1430            .filter(|n| n.kind() == "identifier")
1431            .map(|n| n.id())
1432            .collect();
1433        let actual: Vec<usize> = found.iter().map(Node::id).collect();
1434        assert_eq!(actual, expected);
1435        assert!(
1436            found.len() >= 3,
1437            "expected at least the `main`, `x` decl, and `x` uses"
1438        );
1439        assert!(
1440            found.iter().all(|n| n.kind() == "identifier"),
1441            "every collected node must match the requested kind"
1442        );
1443
1444        // An absent kind yields nothing; a multi-kind filter unions.
1445        assert!(root.descendants_by_kind(&["no_such_kind"]).is_empty());
1446        assert!(
1447            root.descendants_by_kind(&["identifier", "number_literal"])
1448                .len()
1449                > found.len(),
1450            "adding `number_literal` must widen the match set"
1451        );
1452    }
1453
1454    /// Drains `iter`, holding it to the `ExactSizeIterator` contract at
1455    /// every step, and returns the `(id, kind_id)` of each child yielded.
1456    ///
1457    /// `len()` must equal the node's `child_count` before the first step,
1458    /// fall by exactly one per yield, and be zero at exhaustion. Both
1459    /// child iterators are checked against it, so the contract is stated
1460    /// once — `children_with` exists to save an allocation, and a
1461    /// separate copy of this is how the two would come to disagree.
1462    #[cfg(feature = "mozjs")]
1463    fn drain_checking_exact_size<'a>(
1464        mut iter: impl ExactSizeIterator<Item = Node<'a>>,
1465        child_count: usize,
1466        what: &str,
1467        kind: &str,
1468    ) -> Vec<(usize, u16)> {
1469        assert_eq!(
1470            iter.len(),
1471            child_count,
1472            "{what}().len() disagreed with child_count at kind {kind}"
1473        );
1474        let mut remaining = child_count;
1475        let mut drained = Vec::with_capacity(remaining);
1476        while let Some(child) = iter.next() {
1477            remaining -= 1;
1478            assert_eq!(
1479                iter.len(),
1480                remaining,
1481                "{what}() size_hint drifted mid-iteration at kind {kind}"
1482            );
1483            drained.push((child.id(), child.kind_id()));
1484        }
1485        assert_eq!(
1486            iter.len(),
1487            0,
1488            "{what}() was not drained to zero len at kind {kind}"
1489        );
1490        drained
1491    }
1492
1493    /// Ancestor ids yielded by `ancestors`, nearest first.
1494    #[cfg(any(
1495        feature = "c",
1496        feature = "elixir",
1497        feature = "java",
1498        feature = "javascript",
1499        feature = "kotlin",
1500        feature = "python",
1501        feature = "ruby",
1502        feature = "rust",
1503    ))]
1504    fn ancestor_ids(ancestors: Ancestors<'_, '_>, node: &Node<'_>) -> Vec<usize> {
1505        ancestors.iter(node).map(|(a, _)| a.id()).collect()
1506    }
1507
1508    /// A known chain must answer every ancestor question exactly as
1509    /// climbing with `Node::parent` does — that equivalence is the whole
1510    /// premise of #1084, and it is what lets the predicates keep their
1511    /// original logic while dropping the `O(depth)` lookup.
1512    ///
1513    /// Checked node-by-node over one fixture per grammar family that
1514    /// actually consults an ancestor: C-family (`is_else_if` via the
1515    /// parent clause, `loc`'s declaration gate), JVM-family
1516    /// (`is_else_if` via the preceding `else` token), Python (the
1517    /// grandparent shape), and Elixir (`quote` templates).
1518    #[cfg(all(
1519        feature = "c",
1520        feature = "elixir",
1521        feature = "java",
1522        feature = "javascript",
1523        feature = "kotlin",
1524        feature = "python",
1525        feature = "ruby",
1526        feature = "rust",
1527    ))]
1528    #[test]
1529    fn a_known_chain_answers_exactly_what_climbing_answers() {
1530        /// `must_nest` names kinds that have to appear *inside another
1531        /// node of the same kind* in the fixture. `visited > 20` alone
1532        /// does not keep a fixture honest: a grammar bump that flattened
1533        /// the nesting a row was added for would leave a large,
1534        /// clean-parsing tree that no longer exercises the shape, and
1535        /// the parity assertions would keep passing over it.
1536        #[cfg(any(
1537            feature = "c",
1538            feature = "elixir",
1539            feature = "java",
1540            feature = "javascript",
1541            feature = "kotlin",
1542            feature = "python",
1543            feature = "ruby",
1544            feature = "rust",
1545        ))]
1546        fn assert_parity<L: LanguageInfo>(label: &str, code: &[u8], must_nest: &[&str]) {
1547            let mut nested_seen = vec![false; must_nest.len()];
1548            let visited = for_each_node_with_chain::<L>(code, |node, chain| {
1549                for (slot, kind) in nested_seen.iter_mut().zip(must_nest) {
1550                    *slot |= node.kind() == *kind
1551                        && chain.iter().any(|ancestor| ancestor.kind() == *kind);
1552                }
1553                let known = Ancestors::known(chain);
1554                let climbing = Ancestors::unknown();
1555                assert_eq!(
1556                    known.parent(node).map(|p| p.id()),
1557                    climbing.parent(node).map(|p| p.id()),
1558                    "{label}: parent of {} disagrees",
1559                    node.kind()
1560                );
1561                assert_eq!(
1562                    known.previous_sibling(node).map(|p| p.id()),
1563                    climbing.previous_sibling(node).map(|p| p.id()),
1564                    "{label}: previous sibling of {} disagrees",
1565                    node.kind()
1566                );
1567                assert_eq!(
1568                    ancestor_ids(known, node),
1569                    ancestor_ids(climbing, node),
1570                    "{label}: ancestor chain of {} disagrees",
1571                    node.kind()
1572                );
1573                // Each ancestor is handed *its* own chain, so a
1574                // predicate applied one level up stays as cheap and as
1575                // correct as one applied to the node itself.
1576                for (ancestor, above) in known.iter(node) {
1577                    assert_eq!(
1578                        above.parent(&ancestor).map(|p| p.id()),
1579                        ancestor.parent().map(|p| p.id()),
1580                        "{label}: sub-chain handed to {} is not its own",
1581                        ancestor.kind()
1582                    );
1583                }
1584            });
1585            assert!(visited > 20, "{label}: fixture is too small to prove much");
1586            for (found, kind) in nested_seen.iter().zip(must_nest) {
1587                assert!(
1588                    found,
1589                    "{label}: no `{kind}` sits inside another `{kind}`, so the \
1590                     fixture no longer exercises the nesting it was added for"
1591                );
1592            }
1593        }
1594
1595        assert_parity::<crate::langs::CCode>(
1596            "c",
1597            b"int main() { if (a) { int x; } else if (b) { for (int i = 0; i < 2; i++) x; } }",
1598            &[],
1599        );
1600        assert_parity::<crate::langs::JavaCode>(
1601            "java",
1602            b"class A { void m() { if (a) {} else if (b) {} else {} for (int i = 0; i < 2; i++) {} } }",
1603            &[],
1604        );
1605        assert_parity::<crate::langs::PythonCode>(
1606            "python",
1607            b"def f(a, b):\n    if a:\n        pass\n    else:\n        if b:\n            pass\n    return a and b or a\n",
1608            &[],
1609        );
1610        assert_parity::<crate::langs::ElixirCode>(
1611            "elixir",
1612            b"defmodule M do\n  def g do\n    :ok\n  end\n  quote do\n    def f do\n      :ok\n    end\n  end\nend\n",
1613            &["call"],
1614        );
1615
1616        // The shapes #1062 added as consumers, which the four fixtures
1617        // above do not contain: a function nested inside a function
1618        // (every language's `increment_function_depth` arm walks the
1619        // chain looking for one) and the two default-arm checks that
1620        // now read `Ancestors::parent` — Kotlin's `else ->` inside a
1621        // `when` and Ruby's `else` inside a `case`. Parity over the
1622        // machinery is not parity over the shape a caller asks about.
1623        assert_parity::<crate::langs::RustCode>(
1624            "rust",
1625            b"fn f(a: bool) { if a { } else if a { } fn g(b: bool) { if b { } } }\n",
1626            &["function_item"],
1627        );
1628        assert_parity::<crate::langs::KotlinCode>(
1629            "kotlin",
1630            b"fun f(x: Int) {\n    when (x) {\n        1 -> {}\n        else -> {}\n    }\n    fun g() {\n        if (x > 0) {}\n    }\n}\n",
1631            &["function_declaration"],
1632        );
1633        assert_parity::<crate::langs::RubyCode>(
1634            "ruby",
1635            b"def f(x)\n  case x\n  when 1 then 1\n  else 2\n  end\n  def g\n    if x\n    end\n  end\nend\n",
1636            &["method"],
1637        );
1638
1639        // The shape #1088 added as a consumer: the JS-family
1640        // `Checker::is_func` / `is_closure` walk upward from an
1641        // `arrow_function` / `function_expression` looking for the
1642        // binding that names it, and end on `Ancestors::previous_sibling`
1643        // through `has_sibling`. None of the fixtures above contains
1644        // either node.
1645        assert_parity::<crate::langs::JavascriptCode>(
1646            "javascript",
1647            b"const f = a => { a => { g(() => 1); }; };\nconst o = { m: function () { return 1; } };\n",
1648            &["arrow_function"],
1649        );
1650    }
1651
1652    /// The traversals #1112 moved onto [`Node::children_with`] must scan
1653    /// a whole tree on one cursor, not one per node.
1654    ///
1655    /// Nothing in the output says so: `children_with` yields exactly
1656    /// what `children` yields, so every metric, marker, and pre-order
1657    /// assertion in the suite holds just as well with a fresh
1658    /// `TreeCursor` built and freed per visited node. The counter is the
1659    /// only observable, which is why reverting one of these loops has to
1660    /// be a test failure rather than a silent allocation per node. The
1661    /// metric and suppression walks are guarded the same way from
1662    /// `big-code-analysis`, where they live.
1663    ///
1664    /// Seeding a real scan first is what makes it falsifiable: compared
1665    /// against zero these assertions would also pass with `record()`
1666    /// never wired up at all.
1667    // `python` and `rust` were stale: the body builds only `CCode` and
1668    // `MozjsCode`, so naming them kept the test out of builds that could
1669    // run it.
1670    #[cfg(all(feature = "c", feature = "mozjs"))]
1671    #[test]
1672    fn the_converted_traversals_scan_a_tree_on_one_cursor() {
1673        let seed_tree = Tree::new::<crate::langs::CCode>(b"int main() { int a; }");
1674        let _ = seed_tree.get_root().children().count();
1675        assert!(
1676            child_scan_cursors::observed() > 0,
1677            "the seed scan must be counted"
1678        );
1679
1680        // `preorder` over a tree far larger than any per-call constant,
1681        // so "one per node" and "one per walk" cannot be confused.
1682        let tree = Tree::new::<MozjsCode>(
1683            b"const o = { m: (a) => a + 1, n: function () { return [1, 2, 3]; } };\nfoo(o);\n",
1684        );
1685        let before = child_scan_cursors::observed();
1686        let visited = tree.get_root().preorder().count();
1687        assert!(visited > 40, "fixture is too small to prove much");
1688        assert_eq!(
1689            child_scan_cursors::observed(),
1690            before,
1691            "preorder built a cursor per node; it holds one for the walk (#1112)"
1692        );
1693
1694        // The `Search` walk, `act_on_node`. The counter records in
1695        // `children()`, the allocating form, so a walk that hoists its
1696        // cursor records nothing at all and a per-node one records once
1697        // per interior node. Asserting the exact zero is what tells a
1698        // hoisted cursor from a per-node one; a bound like `< nodes / 2`
1699        // would hold for either on a small fixture.
1700        let tree = Tree::new::<MozjsCode>(
1701            b"function f(a) { return { g: (b) => b + 1, h: [1, 2, 3] }; }\nf(2);\n",
1702        );
1703        let root = tree.get_root();
1704        let nodes = root.preorder().count();
1705        assert!(nodes > 30, "fixture is too small to prove much");
1706
1707        let before = child_scan_cursors::observed();
1708        let mut seen = 0_usize;
1709        root.act_on_node(&mut |_, _| seen += 1);
1710        let scans = child_scan_cursors::observed() - before;
1711        assert_eq!(seen, nodes, "act_on_node must visit every node");
1712        assert_eq!(
1713            scans, 0,
1714            "act_on_node built {scans} cursors over {nodes} nodes; it holds one \
1715             for the walk (#1112)"
1716        );
1717    }
1718
1719    /// [`Node::parent_grandparent_match`] must answer `false` when
1720    /// either link is missing, rather than degrading to a
1721    /// single-predicate check.
1722    ///
1723    /// Its doc states that invariant and Python's `Cyclomatic` `else`
1724    /// arm depends on it, but nothing exercised either absent-link
1725    /// return: every call in the suite runs on a node that has both a
1726    /// parent and a grandparent. Both predicates answer `true` here, so
1727    /// a `false` result can only come from the missing link — an
1728    /// implementation that skipped the second `climb.next()` and
1729    /// returned `parent_pred`'s answer would pass every other test and
1730    /// fail this one.
1731    ///
1732    /// Checked through both `Ancestors` constructors: the chain and the
1733    /// climb reach the end by different code paths (`split_last` on an
1734    /// empty slice, versus `Node::parent` returning `None`).
1735    #[cfg(feature = "c")]
1736    #[test]
1737    fn parent_grandparent_match_is_false_when_either_link_is_absent() {
1738        let tree = Tree::new::<crate::langs::CCode>(b"int main() { int a; }");
1739        let root = tree.get_root();
1740        let child = root.children().next().expect("the file has an item");
1741        let grandchild = child
1742            .children()
1743            .next()
1744            .expect("the function definition has children");
1745        let yes: fn(&Node) -> bool = |_| true;
1746
1747        // No parent at all: the root, reached either way.
1748        assert!(!root.parent_grandparent_match(Ancestors::unknown(), yes, yes));
1749        assert!(!root.parent_grandparent_match(Ancestors::known(&[]), yes, yes));
1750
1751        // A parent but no grandparent: a direct child of the root.
1752        assert!(!child.parent_grandparent_match(Ancestors::unknown(), yes, yes));
1753        assert!(!child.parent_grandparent_match(Ancestors::known(&[root]), yes, yes));
1754
1755        // Both links present, so the same predicates now answer `true`.
1756        // Without this the assertions above would also hold for a
1757        // function that always returned `false`.
1758        assert!(grandchild.parent_grandparent_match(Ancestors::unknown(), yes, yes));
1759        assert!(grandchild.parent_grandparent_match(Ancestors::known(&[root, child]), yes, yes));
1760    }
1761
1762    /// [`Node::children_with`] must yield exactly what
1763    /// [`Node::children`] yields — same nodes, same order, same
1764    /// `ExactSizeIterator` length at every step — for every node of a
1765    /// real tree.
1766    ///
1767    /// Checked against the raw `child(i)` walk rather than against
1768    /// `children()`: the two iterators share `ChildScan`, so a
1769    /// comparison between them would pass just as happily if the shared
1770    /// step were wrong. It also covers the reuse itself — one cursor
1771    /// drives every node's scan here, so a `reset` that failed to rewind
1772    /// would show as the second node inheriting the first's position.
1773    #[cfg(feature = "mozjs")]
1774    #[test]
1775    fn children_with_yields_exactly_what_children_does() {
1776        let code = b"const o = { m: (a) => a + 1, n: function () {} }; foo(); ;";
1777        let tree = Tree::new::<MozjsCode>(code);
1778        let root = tree.get_root();
1779
1780        let mut cursor = root.cursor();
1781        let mut leaves = 0;
1782        let mut widest = 0;
1783        for node in root.preorder() {
1784            // Ground truth is the raw `child(i)` walk, not `children()`.
1785            // The two iterators share `ChildScan`, so checking one
1786            // against the other would pass just as happily if the shared
1787            // step were wrong.
1788            let raw = node.as_tree_sitter();
1789            let expected: Vec<_> = (0..raw.child_count() as u32)
1790                .filter_map(|i| raw.child(i))
1791                .map(|c| (c.id(), c.kind_id()))
1792                .collect();
1793
1794            let actual = drain_checking_exact_size(
1795                node.children_with(&mut cursor),
1796                expected.len(),
1797                "children_with",
1798                node.kind(),
1799            );
1800            assert_eq!(
1801                actual,
1802                expected,
1803                "children_with diverged from the child(i) walk at kind {}",
1804                node.kind()
1805            );
1806
1807            leaves += usize::from(expected.is_empty());
1808            widest = widest.max(expected.len());
1809        }
1810        // Both ends of the arity range, else the comparison could hold
1811        // over nothing but one-child wrappers.
1812        assert!(leaves > 0, "fixture must contain childless nodes");
1813        assert!(widest > 2, "fixture must contain a multi-child node");
1814    }
1815
1816    /// `children_after` and `children_before` must yield, for every child
1817    /// of every node, the raw `child(i)` walk on either side of it — and
1818    /// nothing for a node that is not a child.
1819    ///
1820    /// The Kotlin fixture's recovery inserts a zero-width parameter name
1821    /// (`vararg: Int`), which `goto_first_child_for_byte` passes over, so
1822    /// it is the only input that reaches the scan fallback in
1823    /// `seat_on_child`; the count of such children pins that it does.
1824    #[cfg(feature = "kotlin")]
1825    #[test]
1826    fn children_after_and_before_match_the_child_walk() {
1827        let code = b"fun f(vararg: Int, /* c */ b: Int) { g(1, 2, 3) }";
1828        let tree = Tree::new::<crate::langs::KotlinCode>(code);
1829        let root = tree.get_root();
1830
1831        // Each row is (parent kind, child index, after ids, before ids), and
1832        // a non-child row is (kind, after count, before count) against the
1833        // root, so a failing diff names the node without a message argument.
1834        let mut zero_width = 0;
1835        let (mut observed, mut expected) = (Vec::new(), Vec::new());
1836        let (mut stray_observed, mut stray_expected) = (Vec::new(), Vec::new());
1837        for node in root.preorder() {
1838            let raw = node.as_tree_sitter();
1839            let ids: Vec<_> = (0..raw.child_count() as u32)
1840                .filter_map(|i| raw.child(i))
1841                .map(|c| c.id())
1842                .collect();
1843            for (index, child) in node.children().enumerate() {
1844                zero_width += usize::from(child.start_byte() == child.end_byte());
1845                observed.push((
1846                    node.kind(),
1847                    index,
1848                    node.children_after(&child)
1849                        .map(|c| c.id())
1850                        .collect::<Vec<_>>(),
1851                    node.children_before(&child)
1852                        .map(|c| c.id())
1853                        .collect::<Vec<_>>(),
1854                ));
1855                expected.push((
1856                    node.kind(),
1857                    index,
1858                    ids[index + 1..].to_vec(),
1859                    ids[..index].iter().rev().copied().collect(),
1860                ));
1861            }
1862            if node.id() != root.id() {
1863                stray_observed.push((
1864                    node.kind(),
1865                    root.children_after(&node).count(),
1866                    root.children_before(&node).count(),
1867                ));
1868                stray_expected.push((node.kind(), 0, 0));
1869            }
1870        }
1871        assert_eq!(observed, expected);
1872        assert_eq!(stray_observed, stray_expected);
1873        assert!(
1874            zero_width > 0,
1875            "fixture must hold an inserted zero-width child"
1876        );
1877    }
1878
1879    /// The `O(1)` guard [`Ancestors::checked`] keeps on by default must
1880    /// accept every chain a walker really builds — over several grammar
1881    /// families, not just the one fixture a failure would surface in.
1882    ///
1883    /// The assertion that an equal-span pair was seen is what makes the
1884    /// containment non-strict on purpose rather than by luck: a
1885    /// single-child wrapper (`expression_statement` over its expression,
1886    /// say) spans exactly what its child spans, so tightening either
1887    /// bound to `<` would reject a correct chain on most real input.
1888    #[cfg(all(feature = "c", feature = "javascript", feature = "python"))]
1889    #[test]
1890    fn checked_accepts_the_chains_the_walkers_build() {
1891        let mut equal_span_pairs = 0;
1892        let mut check = |node: &Node<'_>, chain: &[Node<'_>]| {
1893            let _ = Ancestors::checked(chain, node);
1894            if let Some(parent) = chain.last()
1895                && parent.start_byte() == node.start_byte()
1896                && parent.end_byte() == node.end_byte()
1897            {
1898                equal_span_pairs += 1;
1899            }
1900        };
1901        let visited = for_each_node_with_chain::<crate::langs::CCode>(
1902            b"int main() { if (a) { int x; } else { f(a, b); } }",
1903            &mut check,
1904        ) + for_each_node_with_chain::<crate::langs::JavascriptCode>(
1905            b"const o = { m: (a) => a + 1 };\nfoo.bar();\n",
1906            &mut check,
1907        ) + for_each_node_with_chain::<crate::langs::PythonCode>(
1908            b"def f(a):\n    if a:\n        return [x for x in a]\n",
1909            &mut check,
1910        );
1911
1912        assert!(visited > 60, "fixtures are too small to prove much");
1913        assert!(
1914            equal_span_pairs > 0,
1915            "no parent spans exactly what its child does, so this fixture set \
1916             cannot tell non-strict containment from strict"
1917        );
1918    }
1919
1920    /// A `push` moved ahead of the per-node computes leaves the node
1921    /// itself as `chain.last()`. Spans alone cannot see that — a node
1922    /// contains itself — so the identity half of the guard is what
1923    /// catches it.
1924    ///
1925    /// Debug-gated because `debug_assert!` compiles out under
1926    /// `--release`, where `checked` degrades to `known` by design.
1927    #[cfg(feature = "c")]
1928    #[test]
1929    #[cfg(debug_assertions)]
1930    #[should_panic(expected = "ancestor chain desynchronised")]
1931    fn checked_rejects_a_chain_ending_in_the_node_itself() {
1932        let tree = Tree::new::<crate::langs::CCode>(b"int main() { int a; }");
1933        let body = tree
1934            .get_root()
1935            .preorder()
1936            .find(|n| n.kind() == "compound_statement")
1937            .expect("fixture has a function body");
1938        let _ = Ancestors::checked(std::slice::from_ref(&body), &body);
1939    }
1940
1941    /// A dropped `truncate` leaves the previous subtree's path in place,
1942    /// so the next node up gets a `chain.last()` from a sibling subtree —
1943    /// disjoint from it in bytes. That is the containment half.
1944    #[cfg(feature = "c")]
1945    #[test]
1946    #[cfg(debug_assertions)]
1947    #[should_panic(expected = "ancestor chain desynchronised")]
1948    fn checked_rejects_a_chain_from_a_disjoint_subtree() {
1949        let tree = Tree::new::<crate::langs::CCode>(b"int main() { int a; int b; }");
1950        let body = tree
1951            .get_root()
1952            .preorder()
1953            .find(|n| n.kind() == "compound_statement")
1954            .expect("fixture has a function body");
1955        let declarations: Vec<Node<'_>> = body
1956            .children()
1957            .filter(|n| n.kind() == "declaration")
1958            .collect();
1959        assert_eq!(declarations.len(), 2, "fixture has two declarations");
1960        // `int a;` neither contains nor equals `int b;`.
1961        let _ = Ancestors::checked(&declarations[..1], &declarations[1]);
1962    }
1963
1964    /// [`Node::has_sibling`] must answer the same whether its parent
1965    /// comes off a known chain or from `Node::parent`.
1966    ///
1967    /// The parent lookup is the only thing #1088 changed here, and it is
1968    /// the half a caller cannot see: `check_if_arrow_func!` folds the
1969    /// answer into a disjunction, so a wrong parent would silently
1970    /// reclassify an arrow function rather than fail. Checked for every
1971    /// node against every kind the fixture contains, plus one that never
1972    /// occurs so the absent-sibling answer is covered too.
1973    #[cfg(feature = "javascript")]
1974    #[test]
1975    fn has_sibling_agrees_between_known_and_climbing() {
1976        // Object-literal methods and an arrow bound to a property are
1977        // the shapes whose `PropertyIdentifier` sibling the JS closure
1978        // check asks about.
1979        let code = b"const o = { m: (a) => a + 1, n: function () {} };\nconst p = a => a;\n";
1980        let mut kinds = std::collections::BTreeSet::new();
1981        for_each_node_with_chain::<crate::langs::JavascriptCode>(code, |node, _| {
1982            kinds.insert(node.kind_id());
1983        });
1984        // An id no node in the fixture carries, so the `false` answer is
1985        // exercised as well as the `true` one.
1986        let absent = u16::MAX;
1987        let mut agreed_true = 0;
1988        let visited =
1989            for_each_node_with_chain::<crate::langs::JavascriptCode>(code, |node, chain| {
1990                for &id in kinds.iter().chain(std::iter::once(&absent)) {
1991                    let known = node.has_sibling(Ancestors::known(chain), id);
1992                    let climbing = node.has_sibling(Ancestors::unknown(), id);
1993                    assert_eq!(
1994                        known,
1995                        climbing,
1996                        "has_sibling({id}) on {} disagrees between chain and climb",
1997                        node.kind()
1998                    );
1999                    agreed_true += usize::from(known);
2000                }
2001            });
2002        assert!(visited > 20, "fixture is too small to prove much");
2003        assert!(
2004            agreed_true > 0,
2005            "every answer was `false`, so the sibling scan never ran to a match"
2006        );
2007    }
2008
2009    /// [`Search::act_on_node`] must hand each node its true ancestry
2010    /// even when the walk starts below the tree root.
2011    ///
2012    /// The seed is what decides this. [`Ancestors`] reads an empty chain
2013    /// as "this node is the root", so seeding empty — which is correct
2014    /// for every caller today (`bca function`, `find` and `count`, whose
2015    /// walks all start at the root) — would report no parent for the
2016    /// subtree root and shift every answer beneath it. For the JS getters
2017    /// that means
2018    /// losing the `variable_declarator` a `function_expression` takes
2019    /// its name from, so the space would silently be named
2020    /// `<anonymous>`.
2021    ///
2022    /// No caller passes a subtree yet, so nothing else would catch this;
2023    /// the fixture below is the guard, and it fails against an empty
2024    /// seed both here and through `Ancestors::checked`'s debug
2025    /// assertion.
2026    #[cfg(feature = "mozjs")]
2027    #[test]
2028    fn act_on_node_hands_a_subtree_its_real_ancestry() {
2029        let code = b"var outer = function () { return 1; };\n";
2030        let tree = Tree::new::<MozjsCode>(code);
2031        let root = tree.get_root();
2032        let subtree = root
2033            .preorder()
2034            .find(|n| n.kind() == "variable_declarator")
2035            .expect("fixture has a variable_declarator");
2036        assert!(
2037            subtree.parent().is_some(),
2038            "the walk must start below the root, else the seed is vacuous"
2039        );
2040
2041        let mut visited = 0;
2042        subtree.act_on_node(&mut |node, ancestors| {
2043            assert_eq!(
2044                ancestors.parent(node).map(|p| p.id()),
2045                node.parent().map(|p| p.id()),
2046                "parent of {} disagrees with the tree",
2047                node.kind()
2048            );
2049            visited += 1;
2050        });
2051        assert!(visited > 3, "subtree is too small to prove much");
2052    }
2053
2054    /// `previous_sibling` must not answer "no previous sibling" when the
2055    /// chain it was handed belongs to a different node.
2056    ///
2057    /// The known path finds the answer by scanning the chain's last
2058    /// entry for `node`; a miss means the caller paired the two wrongly.
2059    /// Reporting `None` there would be a wrong answer dressed as a
2060    /// legitimate one, so the fallback re-asks the tree.
2061    #[cfg(feature = "c")]
2062    #[test]
2063    fn previous_sibling_falls_back_on_a_chain_that_is_not_this_nodes() {
2064        let code = b"int main() { int a; int b; }";
2065        let tree = Tree::new::<crate::langs::CCode>(code);
2066        let root = tree.get_root();
2067        let body = root
2068            .preorder()
2069            .find(|n| n.kind() == "compound_statement")
2070            .expect("fixture has a function body");
2071        let declarations: Vec<Node<'_>> = body
2072            .children()
2073            .filter(|n| n.kind() == "declaration")
2074            .collect();
2075        assert_eq!(declarations.len(), 2, "fixture has two declarations");
2076
2077        let second = declarations[1];
2078        let expected = second
2079            .previous_sibling()
2080            .map(|p| p.id())
2081            .expect("the second declaration has a previous sibling");
2082        // A chain ending in the *root* does not describe `second`, whose
2083        // parent is the function body.
2084        let foreign = [root];
2085        assert_eq!(
2086            Ancestors::known(&foreign)
2087                .previous_sibling(&second)
2088                .map(|p| p.id()),
2089            Some(expected),
2090            "a mismatched chain must fall back, not report `None`"
2091        );
2092        assert!(
2093            Ancestors::known(&[]).previous_sibling(&second).is_none(),
2094            "an empty chain means `second` is the root, which has no siblings"
2095        );
2096    }
2097
2098    /// `count_specific_ancestors` must return the same count whichever
2099    /// way it reaches the ancestors. Uses `loc`'s real C predicate pair
2100    /// (`while`/`for`/`if` header, stopping at the enclosing block), so
2101    /// the fixture exercises both the counted case (the `for`-header
2102    /// declaration) and the stopped case (the block-scoped ones).
2103    #[cfg(feature = "c")]
2104    #[test]
2105    fn count_specific_ancestors_agrees_between_known_and_climbing() {
2106        let code =
2107            b"int main() { int a; if (x) { int b; } for (int i = 0; i < 2; i++) { int c; } }";
2108        let mut counted = 0;
2109        let mut nonzero = 0;
2110        let visited = for_each_node_with_chain::<crate::langs::CCode>(code, |node, chain| {
2111            if node.kind() != "declaration" {
2112                return;
2113            }
2114            let check: fn(&Node) -> bool = |n| {
2115                matches!(
2116                    n.kind(),
2117                    "while_statement" | "for_statement" | "if_statement"
2118                )
2119            };
2120            let stop: fn(&Node) -> bool = |n| n.kind() == "compound_statement";
2121            let known = node.count_specific_ancestors::<crate::langs::CCode>(
2122                Ancestors::known(chain),
2123                check,
2124                stop,
2125            );
2126            let climbing = node.count_specific_ancestors::<crate::langs::CCode>(
2127                Ancestors::unknown(),
2128                check,
2129                stop,
2130            );
2131            assert_eq!(
2132                known,
2133                climbing,
2134                "declaration at row {}: known chain counted {known}, climbing counted {climbing}",
2135                node.start_row()
2136            );
2137            counted += 1;
2138            nonzero += usize::from(known > 0);
2139        });
2140        assert!(visited > 20);
2141        assert_eq!(counted, 4, "fixture must hold four declarations");
2142        assert_eq!(
2143            nonzero, 1,
2144            "only the `for`-header declaration sits under a header with no block between"
2145        );
2146    }
2147}