pub struct Node<'a>(/* private fields */);Expand description
An AST node.
The inner tree_sitter::Node is reached through
Node::as_tree_sitter for advanced use cases that need direct
access to the underlying tree-sitter API; the field itself is
private so a tree-sitter version bump cannot silently reshape
this struct’s public layout.
Implementations§
Source§impl<'a> Node<'a>
impl<'a> Node<'a>
Sourcepub fn as_tree_sitter(&self) -> OtherNode<'a>
pub fn as_tree_sitter(&self) -> OtherNode<'a>
Returns the underlying tree_sitter::Node for callers that
want to drive their own traversal alongside the metric walker.
tree_sitter::Node is Copy, so the node is returned by
value. The returned node borrows the same source-tree lifetime
as self.
The tree-sitter re-export this exposes is value-not-stable:
the underlying pin may bump in any minor release, so node shape
and node-kind ids are not part of this crate’s stability
contract (see the tree_sitter re-export
note in the crate root).
Sourcepub fn has_error(&self) -> bool
pub fn has_error(&self) -> bool
Checks if a node represents a syntax error or contains any syntax errors anywhere within it.
Transitive, which is the trap: asking it of a construct to decide
whether that construct’s own shape is readable also answers true
for an error arbitrarily deep inside one of its children. Where the
question is about a node’s own slot sequence, ask is_error /
is_missing of each slot instead (#1381).
Sourcepub fn is_error(&self) -> bool
pub fn is_error(&self) -> bool
Whether this node is the ERROR node, as opposed to
has_error’s “is, or contains one”.
Sourcepub fn is_missing(&self) -> bool
pub fn is_missing(&self) -> bool
Whether the parser inserted this node during error recovery: a zero-width token the source does not contain, standing in for one the grammar required.
A sibling of is_error rather than a synonym —
a MISSING token is not an ERROR node, but it occupies a child
slot just the same, so a rule reading a child’s index has to
account for both.
Sourcepub fn id(&self) -> usize
pub fn id(&self) -> usize
An id unique to this node within its tree, stable for the tree’s lifetime. Suitable as a map key for per-node walk state.
Sourcepub fn kind(&self) -> &'static str
pub fn kind(&self) -> &'static str
The grammar’s name for this node’s kind ("function_item",
"identifier", …). Several distinct kind_ids
can share one name — see the aliasing note on kind_id.
Sourcepub fn kind_id(&self) -> u16
pub fn kind_id(&self) -> u16
The numeric kind of this node in its grammar’s symbol table.
This is what the per-language token enums in languages
(Rust::FunctionItem as u16, …) and every classifier match on.
The value is an index into the pinned grammar and moves whenever
that grammar is bumped; it is not stable across versions.
Sourcepub fn utf8_text(&self, data: &'a [u8]) -> Option<&'a str>
pub fn utf8_text(&self, data: &'a [u8]) -> Option<&'a str>
The source text this node spans, or None when the span falls
outside data or those bytes are not valid UTF-8.
Indexed with data.get(..) rather than delegating to
tree_sitter::Node::utf8_text, which is
str::from_utf8(&source[start..end]) — the slice panics on a span
reaching past source, before the Result its signature offers
ever exists, so .ok() cannot catch it. Every caller today passes
the same buffer the tree was parsed from and so cannot produce
that span, but the guarantee is one refactor from false: this
crate already runs passes that parse one buffer and read another
(preproc, comment_rm), and panic! is banned in non-test code
whether or not the branch is currently reachable. data.get(..)
is also what .claude/rules/grammar-dispatch.md §10 prescribes
for reading a node’s bytes.
Sourcepub fn start_byte(&self) -> usize
pub fn start_byte(&self) -> usize
The 0-based byte offset where this node starts.
Sourcepub fn start_position(&self) -> (usize, usize)
pub fn start_position(&self) -> (usize, usize)
The 0-based (row, column) where this node starts.
Sourcepub fn end_position(&self) -> (usize, usize)
pub fn end_position(&self) -> (usize, usize)
The 0-based (row, column) one past this node’s end.
Sourcepub fn end_row(&self) -> usize
pub fn end_row(&self) -> usize
The 0-based row of this node’s end position. A node ending at
column 0 does not occupy that row; see end_line.
Sourcepub fn end_line(&self) -> usize
pub fn end_line(&self) -> usize
The 1-based last line this node occupies — the inclusive end of its line span.
Converting tree-sitter’s 0-based end row needs a + 1 only when
the node actually occupies the row it ends on. A node whose end
column is 0 finished at the start of the row below its last
content row, having absorbed the preceding newline, so that row
is not part of its span.
Getting this wrong is invisible in almost every grammar, because
almost every function node ends just past its closing delimiter
and therefore at a column above 0. Perl’s trailing sub does
not: it ends at column 0 of the row below its }, exactly where
the file root ends, and a blanket + 1 then reported it ending a
line past both its enclosing space and EOF (#1163) — the shape
behind the release usize underflow in #1051.
Sourcepub fn parent(&self) -> Option<Node<'a>>
pub fn parent(&self) -> Option<Node<'a>>
Returns this node’s parent.
O(depth), not O(1). tree-sitter stores no parent pointer:
ts_node_parent restarts at the tree root and descends. A single
call in a per-node metric therefore costs O(nodes × depth) over a
walk, and an ancestor loop built on it costs O(depth²) per call.
This has bitten the analyzer for real: #1052 was a per-leaf
successors(node, Node::parent) walk in Tokens that made the
metric O(leaves × depth²), so a 2 KB file of nested parentheses
took ~19 s. Prefer inheriting state downward through the
traversal (see Walk in spaces::compute) over rediscovering it
upward, and where a predicate genuinely needs an ancestor, take
an Ancestors rather than calling this (#1084).
As of #1096 no code the metric, ops, bca function, or
comment-removal walks reach calls this; the remaining callers are
Ancestors itself (the no-chain fallback), the one-off start node
of a dump, and tests. rg '\.parent\(\)' src/ re-checks that.
Sourcepub fn has_sibling(&self, ancestors: Ancestors<'a, '_>, id: u16) -> bool
pub fn has_sibling(&self, ancestors: Ancestors<'a, '_>, id: u16) -> bool
Returns true if this node’s parent has any direct child with
the given grammar kind_id (the parent’s children include this
node itself, so a self-match counts). Delegates to wraps_any
on the parent. This sits on the JS/TS arrow-function
closure-classification hot path (check_if_arrow_func!); see
#521.
ancestors supplies the parent, for Node::parent’s
O(depth) reason (#1088).
Sourcepub fn previous_sibling(&self) -> Option<Node<'a>>
pub fn previous_sibling(&self) -> Option<Node<'a>>
The sibling immediately before this node.
O(depth), not O(1), for Node::parent’s reason:
ts_node__prev_sibling opens with ts_node_parent. Callers on a
walk should use Ancestors::previous_sibling instead (#1096).
Sourcepub fn is_child(&self, id: u16) -> bool
pub fn is_child(&self, id: u16) -> bool
Returns true if any direct child has the given grammar
kind_id. See #217 for the motivating perf finding from the
JS/TS template-literal hot path.
Sourcepub fn wraps_any(&self, ids: &[u16]) -> bool
pub fn wraps_any(&self, ids: &[u16]) -> bool
Returns true if any direct child matches one of the given
grammar kind_ids. The single-id is_child delegates here, so
both share one child scan (the #[inline] makes the
single-element contains collapse to an equality check).
Generalizing the check to a set lets the shared
string-interpolation operand skip declare its rule once (issue
#420).
§Why a cursor rather than child(0) + next_sibling()
#217 replaced the cursor walk with a next_sibling() chain to
dodge the TreeCursor heap allocation, on the premise that a
sibling step is O(1). It is not: ts_node_next_sibling
resolves the parent first, and tree_sitter stores no parent
pointer — it descends from the root — so each step cost
O(depth) and the scan O(children × depth). That made every
caller of this method O(depth) per node, which is the same
defect #1084 removed from the predicates that ask for an
ancestor outright. The cursor iterator is O(children) after one
allocation, and measured faster on real input as well as on the
pathological one: the nom/nested-arrow probe went from
quadratic (17.6 s at depth 4000) to linear (6.3 ms), and a walk
over the 384-file pdf.js corpus dropped from ~443 ms to
~370 ms (#1088).
Sourcepub fn child_count(&self) -> usize
pub fn child_count(&self) -> usize
The number of direct children, named and anonymous alike.
Sourcepub fn is_named(&self) -> bool
pub fn is_named(&self) -> bool
Whether this node is a named grammar production, as opposed to an
anonymous token such as a punctuation or keyword literal. Walks
use it to skip tokens like the leading | in an or-pattern.
Sourcepub fn child_by_field_name(&self, name: &str) -> Option<Node<'a>>
pub fn child_by_field_name(&self, name: &str) -> Option<Node<'a>>
Returns the direct child reached through the grammar field_name,
if any. The child carries the underlying tree lifetime 'a (the
tree_sitter::Node it wraps is Copy and valid for the whole
tree), so callers may hold it past the borrow of &self — matching
the sibling accessors (child, parent, children, …) rather
than over-narrowing to the method-call borrow (see issue #786).
Sourcepub fn child(&self, pos: usize) -> Option<Node<'a>>
pub fn child(&self, pos: usize) -> Option<Node<'a>>
The direct child at index pos, counting named and anonymous
children alike. Prefer child_by_field_name
for any slot the grammar can precede with an optional preamble.
Sourcepub fn field_name_for_child(&self, child_index: u32) -> Option<&'static str>
pub fn field_name_for_child(&self, child_index: u32) -> Option<&'static str>
Returns the tree-sitter grammar field name through which this
node reaches the child at child_index, if any. Used by the
AST builder to thread the parent’s field_name into each child
without a parallel cursor walk.
Sourcepub fn children(&self) -> Children<'a> ⓘ
pub fn children(&self) -> Children<'a> ⓘ
Iterator over this node’s direct children.
Builds a Cursor, which heap-allocates. A loop that visits
many nodes should hoist one cursor and use children_with
instead; see its note for when the difference is worth the
plumbing.
Sourcepub fn children_with<'c>(
&self,
cursor: &'c mut Cursor<'a>,
) -> ChildrenWith<'c, 'a> ⓘ
pub fn children_with<'c>( &self, cursor: &'c mut Cursor<'a>, ) -> ChildrenWith<'c, 'a> ⓘ
children, over a cursor the caller owns.
tree_sitter::TreeCursor heap-allocates its stack when built and
frees it when dropped, so a traversal that calls children
once per visited node pays a malloc/free pair per node.
ts_tree_cursor_reset keeps the allocation, so a loop that can
hoist one cursor pays for it once however many nodes it visits
(#1112).
Worth the plumbing only where a loop visits many nodes. Measured
on the corpus slice, a full metric walk reaches children on
3-6 % of nodes in C++, Rust, JavaScript, and Java, 16 % in C#,
and 60 % in Python, where one scan — the instance-attribute walk
in metrics::npa::python — was 92 % of the total, and
preorder plus the suppression DFS are the rest. Crate-wide
there is one more per-node consumer, output::dump’s renderer,
which no metric walk runs and so is absent from that total.
Predicates
that hold a bare &Node and scan one node’s children keep
children: threading a cursor to them would cross the
Checker / Getter trait surface for one allocation per call.
Sourcepub fn children_after(
&self,
child: &Node<'a>,
) -> impl Iterator<Item = Node<'a>> + use<'a>
pub fn children_after( &self, child: &Node<'a>, ) -> impl Iterator<Item = Node<'a>> + use<'a>
The children after child, nearest first: what
children().skip_while(|c| c.id() != child.id()).skip(1) yields,
and nothing when child is not one of this node’s children.
That scan costs child’s index, so a walk that asks it of every
child of a wide list is quadratic in the list (#1545). This seeks
child by byte instead, which is O(log n) in a list the grammar
stores as a repeat() and still O(index) in a flat one, an
ERROR above all; see Cursor::goto_first_child_for_byte. A
walk that asks of every child of a node that may be flat goes
through Ancestors::siblings_after (#1584).
Sourcepub fn children_before(
&self,
child: &Node<'a>,
) -> impl Iterator<Item = Node<'a>> + use<'a>
pub fn children_before( &self, child: &Node<'a>, ) -> impl Iterator<Item = Node<'a>> + use<'a>
The children before child, nearest first, and nothing when
child is not one of this node’s children.
child’s index comes from Cursor::goto_first_child_for_byte
and each sibling from Node::child, both O(log n) in the
child count where the list is a repeat(), stored as a balanced
tree of hidden nodes, and both O(index) where it is flat, as an
ERROR built by skipping tokens is. A walk that asks of every
child of such a node goes through Ancestors::siblings_before
(#1584).
Sourcepub fn cursor(&self) -> Cursor<'a>
pub fn cursor(&self) -> Cursor<'a>
A fresh cursor positioned on this node, for the traversals that
reuse one cursor across many child scans
(children_with).
Sourcepub fn count_specific_ancestors<C: Checker>(
&self,
ancestors: Ancestors<'a, '_>,
check: fn(&Node<'_>) -> bool,
stop: fn(&Node<'_>) -> bool,
) -> usize
pub fn count_specific_ancestors<C: Checker>( &self, ancestors: Ancestors<'a, '_>, check: fn(&Node<'_>) -> bool, stop: fn(&Node<'_>) -> bool, ) -> usize
Counts this node’s ancestors satisfying check, walking upward
from the parent and stopping at (and excluding) the first
ancestor satisfying stop. An ancestor that is the if of an
else if chain never counts — it is a continuation of the
branch above it, not a new enclosing one.
ancestors is the chain the caller descended through. Passing
Ancestors::unknown is always correct and answers identically;
it just pays O(depth) per step instead of O(1).
Sourcepub fn parent_grandparent_match(
&self,
ancestors: Ancestors<'a, '_>,
parent_pred: fn(&Node<'_>) -> bool,
grand_pred: fn(&Node<'_>) -> bool,
) -> bool
pub fn parent_grandparent_match( &self, ancestors: Ancestors<'a, '_>, parent_pred: fn(&Node<'_>) -> bool, grand_pred: fn(&Node<'_>) -> bool, ) -> bool
Returns true iff this node’s parent satisfies parent_pred
AND that parent’s own parent (this node’s grandparent)
satisfies grand_pred. Returns false as soon as either link
is absent or its predicate fails, so a misordered predicate
cannot silently degrade to a single-predicate check.
ancestors is the chain the caller descended through. Passing
Ancestors::unknown is always correct and answers
identically; it just pays Node::parent’s O(depth) for each
of the two links, which on a per-node metric arm is quadratic in
nesting depth (#1096).
Sourcepub fn preorder(&self) -> Preorder<'a> ⓘ
pub fn preorder(&self) -> Preorder<'a> ⓘ
Returns a pre-order iterator over this node and all of its descendants (this node first, then each child subtree left to right).
The traversal is allocation-light: it reuses one work stack and
one cursor, and visits each node exactly once, so a full walk is
O(n) in the subtree size and allocates only the stack’s growth.
Building a fresh cursor per visited node instead would cost a
malloc/free pair per node (#1112). Every yielded Node
carries the underlying tree lifetime 'a, so callers may collect
or retain the handles.
This is the Rust counterpart of the Python Node.walk() binding
(issue #728): the binding wraps each yielded node, so Rust and
Python share one traversal order.
Sourcepub fn descendants_by_kind(&self, kinds: &[&str]) -> Vec<Node<'a>>
pub fn descendants_by_kind(&self, kinds: &[&str]) -> Vec<Node<'a>>
Collects every node in this subtree (this node included) whose
kind is listed in kinds, in
pre-order.
Membership is an exact match against the raw grammar kind — the
same unaltered vocabulary big_code_analysis::Ast::root_node exposes, not
the Alterator-curated kinds big_code_analysis::Ast::dump emits. This is
the Rust counterpart of the Python Node.descendants_by_kind()
binding (issue #728).
Trait Implementations§
impl<'a> Copy for Node<'a>
Source§impl<'a> Search<'a> for Node<'a>
impl<'a> Search<'a> for Node<'a>
Source§fn act_on_node(&self, action: &mut dyn FnMut(&Node<'a>, Ancestors<'a, '_>))
fn act_on_node(&self, action: &mut dyn FnMut(&Node<'a>, Ancestors<'a, '_>))
Node::parent (#1088).Source§fn first_child(&self, pred: fn(u16) -> bool) -> Option<Node<'a>>
fn first_child(&self, pred: fn(u16) -> bool) -> Option<Node<'a>>
kind_id satisfies pred.