Skip to main content

Node

Struct Node 

Source
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>

Source

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).

Source

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).

Source

pub fn is_error(&self) -> bool

Whether this node is the ERROR node, as opposed to has_error’s “is, or contains one”.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn start_byte(&self) -> usize

The 0-based byte offset where this node starts.

Source

pub fn end_byte(&self) -> usize

The 0-based byte offset one past this node’s last byte.

Source

pub fn start_position(&self) -> (usize, usize)

The 0-based (row, column) where this node starts.

Source

pub fn end_position(&self) -> (usize, usize)

The 0-based (row, column) one past this node’s end.

Source

pub fn start_row(&self) -> usize

The 0-based row this node starts on.

Source

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.

Source

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.

Source

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.

Source

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).

Source

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).

Source

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.

Source

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).

Source

pub fn child_count(&self) -> usize

The number of direct children, named and anonymous alike.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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).

Source

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).

Source

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).

Source

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).

Source

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).

Source

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.

Source

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§

Source§

impl<'a> Clone for Node<'a>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Copy for Node<'a>

Source§

impl<'a> Debug for Node<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'a> Search<'a> for Node<'a>

Source§

fn act_on_node(&self, action: &mut dyn FnMut(&Node<'a>, Ancestors<'a, '_>))

Visits every node of the subtree in source order, handing the action each node’s ancestor chain alongside it so a predicate it applies stays off Node::parent (#1088).
Source§

fn first_child(&self, pred: fn(u16) -> bool) -> Option<Node<'a>>

The first direct child whose kind_id satisfies pred.
Source§

fn act_on_child(&self, action: &mut dyn FnMut(&Node<'a>))

Visits each direct child in source order.

Auto Trait Implementations§

§

impl<'a> Freeze for Node<'a>

§

impl<'a> RefUnwindSafe for Node<'a>

§

impl<'a> Send for Node<'a>

§

impl<'a> Sync for Node<'a>

§

impl<'a> Unpin for Node<'a>

§

impl<'a> UnsafeUnpin for Node<'a>

§

impl<'a> UnwindSafe for Node<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.