package inspector
import "golang.org/x/tools/go/ast/inspector"
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 ¶
- func All[N interface { *S ast.Node }, S any](in *Inspector) iter.Seq[N]
- type Cursor
- func (c Cursor) Child(n ast.Node) Cursor
- func (c Cursor) ChildAt(k edge.Kind, idx int) Cursor
- func (c Cursor) Children() iter.Seq[Cursor]
- func (c Cursor) Contains(c2 Cursor) bool
- func (c Cursor) Enclosing(types ...ast.Node) iter.Seq[Cursor]
- func (c Cursor) FindByPos(start, end token.Pos) (Cursor, bool)
- func (c Cursor) FindNode(n ast.Node) (Cursor, bool)
- func (c Cursor) FirstChild() (Cursor, bool)
- func (c Cursor) Index() int32
- func (c Cursor) Inspect(types []ast.Node, f func(c Cursor) (descend bool))
- func (c Cursor) Inspector() *Inspector
- func (c Cursor) LastChild() (Cursor, bool)
- func (c Cursor) NextSibling() (Cursor, bool)
- func (c Cursor) Node() ast.Node
- func (c Cursor) Parent() Cursor
- func (c Cursor) ParentEdge() (edge.Kind, int)
- func (c Cursor) Preorder(types ...ast.Node) iter.Seq[Cursor]
- func (c Cursor) PrevSibling() (Cursor, bool)
- func (c Cursor) String() string
- type Inspector
- func New(files []*ast.File) *Inspector
- func (in *Inspector) At(index int32) Cursor
- func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool))
- func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node))
- func (in *Inspector) PreorderSeq(types ...ast.Node) iter.Seq[ast.Node]
- func (in *Inspector) Root() Cursor
- func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool))
Functions ¶
func All ¶
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 ¶
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:
- for ancestors, use Cursor.Parent and Cursor.Enclosing;
- for children, use Cursor.Child, Cursor.Children, Cursor.FirstChild, and Cursor.LastChild;
- for siblings, use Cursor.PrevSibling and Cursor.NextSibling;
- for descendants, use Cursor.FindByPos, Cursor.FindNode, Cursor.Inspect, and Cursor.Preorder.
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 ¶
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 ¶
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 ¶
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:
- ast.ValueSpec (Names, Values)
- ast.CompositeLit (Type, Elts)
- ast.IndexListExpr (X, Indices)
- ast.CallExpr (Fun, Args)
- ast.AssignStmt (Lhs, Rhs)
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
FirstChild returns the first direct child of the current node, or zero if it has no children.
func (Cursor) Index ¶
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 ¶
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 ¶
Inspector returns the cursor's Inspector.
func (Cursor) LastChild ¶
LastChild returns the last direct child of the current node, or zero if it has no children.
func (Cursor) NextSibling ¶
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 ¶
Node returns the node at the current cursor position, or nil for the cursor returned by Inspector.Root.
func (Cursor) Parent ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
New returns an Inspector for the specified syntax trees.
func (*Inspector) At ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 })
Source Files ¶
cursor.go inspector.go iter.go typeof.go walk.go
- Version
- v0.34.0 (latest)
- Published
- Jun 5, 2025
- Platform
- linux/amd64
- Imports
- 8 packages
- Last checked
- 20 seconds ago –
Tools for package owners.