inspector

package
v0.34.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 5, 2025 License: BSD-3-Clause Imports: 8 Imported by: 2,156

Documentation

Overview

Package inspector provides helper functions for traversal over the syntax trees of a package, including node filtering by type, and materialization of the traversal stack.

During construction, the inspector does a complete traversal and builds a list of push/pop events and their node type. Subsequent method calls that request a traversal scan this list, rather than walk the AST, and perform type filtering using efficient bit sets. This representation is sometimes called a "balanced parenthesis tree."

Experiments suggest the inspector's traversals are about 2.5x faster than ast.Inspect, but it may take around 5 traversals for this benefit to amortize the inspector's construction cost. If efficiency is the primary concern, do not use Inspector for one-off traversals.

The Cursor type provides a more flexible API for efficient navigation of syntax trees in all four "cardinal directions". For example, traversals may be nested, so you can find each node of type A and then search within it for nodes of type B. Or you can traverse from a node to its immediate neighbors: its parent, its previous and next sibling, or its first and last child. We recommend using methods of Cursor in preference to Inspector where possible.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func All added in v0.26.0

func All[N interface {
	*S
	ast.Node
}, S any](in *Inspector) iter.Seq[N]

All[N] returns an iterator over all the nodes of type N. N must be a pointer-to-struct type that implements ast.Node.

Example:

for call := range All[*ast.CallExpr](in) { ... }

Types

type Cursor added in v0.34.0

type Cursor struct {
	// contains filtered or unexported fields
}

A Cursor represents an ast.Node. It is immutable.

Two Cursors compare equal if they represent the same node.

Call Inspector.Root to obtain a valid cursor for the virtual root node of the traversal.

Use the following methods to navigate efficiently around the tree:

Use the Cursor.ChildAt and Cursor.ParentEdge methods for information about the edges in a tree: which field (and slice element) of the parent node holds the child.

func (Cursor) Child added in v0.34.0

func (c Cursor) Child(n ast.Node) Cursor

Child returns the cursor for n, which must be a direct child of c's Node.

Child must not be called on the Root node (whose Cursor.Node returns nil).

func (Cursor) ChildAt added in v0.34.0

func (c Cursor) ChildAt(k edge.Kind, idx int) Cursor

ChildAt returns the cursor for the child of the current node identified by its edge and index. The index must be -1 if the edge.Kind is not a slice. The indicated child node must exist.

ChildAt must not be called on the Root node (whose Cursor.Node returns nil).

Invariant: c.Parent().ChildAt(c.ParentEdge()) == c.

func (Cursor) Children added in v0.34.0

func (c Cursor) Children() iter.Seq[Cursor]

Children returns an iterator over the direct children of the current node, if any.

When using Children, NextChild, and PrevChild, bear in mind that a Node's children may come from different fields, some of which may be lists of nodes without a distinguished intervening container such as ast.BlockStmt.

For example, ast.CaseClause has a field List of expressions and a field Body of statements, so the children of a CaseClause are a mix of expressions and statements. Other nodes that have "uncontained" list fields include:

So, do not assume that the previous sibling of an ast.Stmt is also an ast.Stmt, or if it is, that they are executed sequentially, unless you have established that, say, its parent is a BlockStmt or its Cursor.ParentEdge is edge.BlockStmt_List. For example, given "for S1; ; S2 {}", the predecessor of S2 is S1, even though they are not executed in sequence.

func (Cursor) Contains added in v0.34.0

func (c Cursor) Contains(c2 Cursor) bool

Contains reports whether c contains or is equal to c2.

Both Cursors must belong to the same Inspector; neither may be its Root node.

func (Cursor) Enclosing added in v0.34.0

func (c Cursor) Enclosing(types ...ast.Node) iter.Seq[Cursor]

Enclosing returns an iterator over the nodes enclosing the current current node, starting with the Cursor itself.

Enclosing must not be called on the Root node (whose Cursor.Node returns nil).

The types argument, if non-empty, enables type-based filtering of events: the sequence includes only enclosing nodes whose type matches an element of the types slice.

func (Cursor) FindByPos added in v0.34.0

func (c Cursor) FindByPos(start, end token.Pos) (Cursor, bool)

FindByPos returns the cursor for the innermost node n in the tree rooted at c such that n.Pos() <= start && end <= n.End(). (For an *ast.File, it uses the bounds n.FileStart-n.FileEnd.)

It returns zero if none is found. Precondition: start <= end.

See also [astutil.PathEnclosingInterval], which tolerates adjoining whitespace.

func (Cursor) FindNode added in v0.34.0

func (c Cursor) FindNode(n ast.Node) (Cursor, bool)

FindNode returns the cursor for node n if it belongs to the subtree rooted at c. It returns zero if n is not found.

func (Cursor) FirstChild added in v0.34.0

func (c Cursor) FirstChild() (Cursor, bool)

FirstChild returns the first direct child of the current node, or zero if it has no children.

func (Cursor) Index added in v0.34.0

func (c Cursor) Index() int32

Index returns the index of this cursor position within the package.

Clients should not assume anything about the numeric Index value except that it increases monotonically throughout the traversal. It is provided for use with [At].

Index must not be called on the Root node.

func (Cursor) Inspect added in v0.34.0

func (c Cursor) Inspect(types []ast.Node, f func(c Cursor) (descend bool))

Inspect visits the nodes of the subtree represented by c in depth-first order. It calls f(n) for each node n before it visits n's children. If f returns true, Inspect invokes f recursively for each of the non-nil children of the node.

Each node is represented by a Cursor that allows access to the Node, but may also be used to start a new traversal, or to obtain the stack of nodes enclosing the cursor.

The complete traversal sequence is determined by ast.Inspect. The types argument, if non-empty, enables type-based filtering of events. The function f if is called only for nodes whose type matches an element of the types slice.

func (Cursor) Inspector added in v0.34.0

func (c Cursor) Inspector() *Inspector

Inspector returns the cursor's Inspector.

func (Cursor) LastChild added in v0.34.0

func (c Cursor) LastChild() (Cursor, bool)

LastChild returns the last direct child of the current node, or zero if it has no children.

func (Cursor) NextSibling added in v0.34.0

func (c Cursor) NextSibling() (Cursor, bool)

NextSibling returns the cursor for the next sibling node in the same list (for example, of files, decls, specs, statements, fields, or expressions) as the current node. It returns (zero, false) if the node is the last node in the list, or is not part of a list.

NextSibling must not be called on the Root node.

See note at Cursor.Children.

func (Cursor) Node added in v0.34.0

func (c Cursor) Node() ast.Node

Node returns the node at the current cursor position, or nil for the cursor returned by Inspector.Root.

func (Cursor) Parent added in v0.34.0

func (c Cursor) Parent() Cursor

Parent returns the parent of the current node.

Parent must not be called on the Root node (whose Cursor.Node returns nil).

func (Cursor) ParentEdge added in v0.34.0

func (c Cursor) ParentEdge() (edge.Kind, int)

ParentEdge returns the identity of the field in the parent node that holds this cursor's node, and if it is a list, the index within it.

For example, f(x, y) is a CallExpr whose three children are Idents. f has edge kind edge.CallExpr_Fun and index -1. x and y have kind edge.CallExpr_Args and indices 0 and 1, respectively.

If called on a child of the Root node, it returns (edge.Invalid, -1).

ParentEdge must not be called on the Root node (whose Cursor.Node returns nil).

func (Cursor) Preorder added in v0.34.0

func (c Cursor) Preorder(types ...ast.Node) iter.Seq[Cursor]

Preorder returns an iterator over the nodes of the subtree represented by c in depth-first order. Each node in the sequence is represented by a Cursor that allows access to the Node, but may also be used to start a new traversal, or to obtain the stack of nodes enclosing the cursor.

The traversal sequence is determined by ast.Inspect. The types argument, if non-empty, enables type-based filtering of events. The function f if is called only for nodes whose type matches an element of the types slice.

If you need control over descent into subtrees, or need both pre- and post-order notifications, use Cursor.Inspect

func (Cursor) PrevSibling added in v0.34.0

func (c Cursor) PrevSibling() (Cursor, bool)

PrevSibling returns the cursor for the previous sibling node in the same list (for example, of files, decls, specs, statements, fields, or expressions) as the current node. It returns zero if the node is the first node in the list, or is not part of a list.

It must not be called on the Root node.

See note at Cursor.Children.

func (Cursor) String added in v0.34.0

func (c Cursor) String() string

String returns information about the cursor's node, if any.

type Inspector

type Inspector struct {
	// contains filtered or unexported fields
}

An Inspector provides methods for inspecting (traversing) the syntax trees of a package.

func New

func New(files []*ast.File) *Inspector

New returns an Inspector for the specified syntax trees.

func (*Inspector) At added in v0.34.0

func (in *Inspector) At(index int32) Cursor

At returns the cursor at the specified index in the traversal, which must have been obtained from Cursor.Index on a Cursor belonging to the same Inspector (see Cursor.Inspector).

func (*Inspector) Nodes

func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool))

Nodes visits the nodes of the files supplied to New in depth-first order. It calls f(n, true) for each node n before it visits n's children. If f returns true, Nodes invokes f recursively for each of the non-nil children of the node, followed by a call of f(n, false).

The complete traversal sequence is determined by ast.Inspect. The types argument, if non-empty, enables type-based filtering of events. The function f if is called only for nodes whose type matches an element of the types slice.

The Cursor.Inspect method provides a richer alternative interface. Example:

in.Root().Inspect(types, func(c Cursor) bool {
	...
	return true
}

func (*Inspector) Preorder

func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node))

Preorder visits all the nodes of the files supplied to New in depth-first order. It calls f(n) for each node n before it visits n's children.

The complete traversal sequence is determined by ast.Inspect. The types argument, if non-empty, enables type-based filtering of events. The function f is called only for nodes whose type matches an element of the types slice.

The Cursor.Preorder method provides a richer alternative interface. Example:

for c := range in.Root().Preorder(types) { ... }

func (*Inspector) PreorderSeq added in v0.26.0

func (in *Inspector) PreorderSeq(types ...ast.Node) iter.Seq[ast.Node]

PreorderSeq returns an iterator that visits all the nodes of the files supplied to New in depth-first order. It visits each node n before n's children. The complete traversal sequence is determined by ast.Inspect.

The types argument, if non-empty, enables type-based filtering of events: only nodes whose type matches an element of the types slice are included in the sequence.

func (*Inspector) Root added in v0.34.0

func (in *Inspector) Root() Cursor

Root returns a cursor for the virtual root node, whose children are the files provided to New.

Its Cursor.Node and [Cursor.Stack] methods return nil.

func (*Inspector) WithStack

func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool))

WithStack visits nodes in a similar manner to Nodes, but it supplies each call to f an additional argument, the current traversal stack. The stack's first element is the outermost node, an *ast.File; its last is the innermost, n.

The Cursor.Inspect method provides a richer alternative interface. Example:

in.Root().Inspect(types, func(c Cursor) bool {
	stack := slices.Collect(c.Enclosing())
	...
	return true
})