Source File
cursor.go
Belonging Package
golang.org/x/tools/go/ast/inspector
// Copyright 2025 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package inspectorimport ()// A Cursor represents an [ast.Node]. It is immutable.//// Two Cursors compare equal if they represent the same node.//// The zero value of Cursor is not valid.//// Call [Inspector.Root] to obtain a cursor for the virtual root node// of the traversal. This is the sole valid cursor for which [Cursor.Node]// returns nil.//// 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.type Cursor struct {in *Inspectorindex int32 // index of push node; -1 for virtual root node}// Root returns a valid cursor for the virtual root node,// whose children are the files provided to [New].//// Its [Cursor.Node] method return nil.func ( *Inspector) () Cursor {return Cursor{, -1}}// 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) ( int32) Cursor {if < 0 {panic("negative index")}if int() >= len(.events) {panic("index out of range for this inspector")}if .events[].index < {panic("invalid index") // (a push, not a pop)}return Cursor{, }}// Valid reports whether the cursor is valid.// The zero value of cursor is invalid.// Unless otherwise documented, it is not safe to call// any other method on an invalid cursor.func ( Cursor) () bool {return .in != nil}// Inspector returns the cursor's Inspector.// It returns nil if the Cursor is not valid.func ( Cursor) () *Inspector { return .in }// 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 [Inspector.At].//// Index must not be called on the Root node.func ( Cursor) () int32 {if .index < 0 {panic("Index called on Root node")}return .index}// Node returns the node at the current cursor position,// or nil for the cursor returned by [Inspector.Root].func ( Cursor) () ast.Node {if .index < 0 {return nil}return .in.events[.index].node}// String returns information about the cursor's node, if any.func ( Cursor) () string {if !.Valid() {return "(invalid)"}if .index < 0 {return "(root)"}return reflect.TypeOf(.Node()).String()}// indices return the [start, end) half-open interval of event indices.func ( Cursor) () (int32, int32) {if .index < 0 {return 0, int32(len(.in.events)) // root: all events} else {return .index, .in.events[.index].index + 1 // just one subtree}}// 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) ( ...ast.Node) iter.Seq[Cursor] {:= maskOf()return func( func(Cursor) bool) {:= .in.eventsfor , := .indices(); < ; {:= []if .index > { // push?if .typ& != 0 && !(Cursor{.in, }) {break}:= .indexif [].typ& == 0 {// Subtree does not contain types: skip.= + 1continue}}++}}}// 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) ( []ast.Node, func( Cursor) ( bool)) {:= maskOf():= .in.eventsfor , := .indices(); < ; {:= []if .index > {// push:= .indexif .typ& != 0 && !(Cursor{.in, }) ||[].typ& == 0 {// The user opted not to descend, or the// subtree does not contain types:// skip past the pop.= + 1continue}}++}}// 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) ( ...ast.Node) iter.Seq[Cursor] {if .index < 0 {panic("Cursor.Enclosing called on Root node")}:= maskOf()return func( func(Cursor) bool) {:= .in.eventsfor := .index; >= 0; = [].parent {if [].typ& != 0 && !(Cursor{.in, }) {break}}}}// Parent returns the parent of the current node.//// Parent must not be called on the Root node (whose [Cursor.Node] returns nil).func ( Cursor) () Cursor {if .index < 0 {panic("Cursor.Parent called on Root node")}return Cursor{.in, .in.events[.index].parent}}// 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) () (edge.Kind, int) {if .index < 0 {panic("Cursor.ParentEdge called on Root node")}:= .in.events:= [.index].indexreturn unpackEdgeKindAndIndex([].parent)}// ParentEdgeKind returns the kind component of the result of [Cursor.ParentEdge].func ( Cursor) () edge.Kind {, := .ParentEdge()return}// ParentEdgeIndex returns the index component of the result of [Cursor.ParentEdge].func ( Cursor) () int {, := .ParentEdge()return}// 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) ( edge.Kind, int) Cursor {:= packEdgeKindAndIndex(, )// Unfortunately there's no shortcut to looping.:= .in.events:= .index + 1for {:= [].indexif < {break}if [].parent == {return Cursor{.in, }}= + 1}panic(fmt.Sprintf("ChildAt(%v, %d): no such child of %v", , , ))}// 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) ( ast.Node) Cursor {if .index < 0 {panic("Cursor.Child called on Root node")}if false {// reference implementationfor := range .Children() {if .Node() == {return}}} else {// optimized implementation:= .in.eventsfor := .index + 1; [].index > ; = [].index + 1 {if [].node == {return Cursor{.in, }}}}panic(fmt.Sprintf("Child(%T): not a child of %v", , ))}// 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) () (Cursor, bool) {if .index < 0 {panic("Cursor.NextSibling called on Root node")}:= .in.events:= [.index].index + 1 // after corresponding popif < int32(len()) {if [].index > { // push?return Cursor{.in, }, true}}return Cursor{}, false}// 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) () (Cursor, bool) {if .index < 0 {panic("Cursor.PrevSibling called on Root node")}:= .in.events:= .index - 1if >= 0 {if := [].index; < { // pop?return Cursor{.in, }, true}}return Cursor{}, false}// FirstChild returns the first direct child of the current node,// or zero if it has no children.func ( Cursor) () (Cursor, bool) {:= .in.events:= .index + 1 // i=0 if c is rootif < int32(len()) && [].index > { // push?return Cursor{.in, }, true}return Cursor{}, false}// LastChild returns the last direct child of the current node,// or zero if it has no children.func ( Cursor) () (Cursor, bool) {:= .in.eventsif .index < 0 { // root?if len() > 0 {// return push of final event (a pop)return Cursor{.in, [len()-1].index}, true}} else {:= [.index].index - 1 // before corresponding pop// Inv: j == c.index if c has no children// or j is last child's pop.if > .index { // c has childrenreturn Cursor{.in, [].index}, true}}return Cursor{}, false}// 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) () iter.Seq[Cursor] {return func( func(Cursor) bool) {, := .FirstChild()for && () {, = .NextSibling()}}}// 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) ( Cursor) bool {if .in != .in {panic("different inspectors")}:= .in.eventsreturn .index <= .index && [.index].index <= [.index].index}// 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) ( ast.Node) (Cursor, bool) {// FindNode is equivalent to this code,// but more convenient and 15-20% faster:if false {for := range .Preorder() {if .Node() == {return , true}}return Cursor{}, false}// TODO(adonovan): opt: should we assume Node.Pos is accurate// and combine type-based filtering with position filtering// like FindByPos?:= maskOf([]ast.Node{}):= .in.eventsfor , := .indices(); < ; ++ {:= []if .index > { // push?if .typ& != 0 && .node == {return Cursor{.in, }, true}:= .indexif [].typ& == 0 {// Subtree does not contain type of n: skip.=}}}return Cursor{}, false}// 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.)//// An empty range (start == end) between two adjacent nodes is// considered to belong to the first node.//// It returns zero if none is found.// Precondition: start <= end.//// See also [astutil.PathEnclosingInterval], which// tolerates adjoining whitespace.func ( Cursor) (, token.Pos) (Cursor, bool) {if < {panic("end < start")}:= .in.events// This algorithm could be implemented using c.Inspect,// but it is about 2.5x slower.// best is the push-index of the latest (=innermost) node containing range.// (Beware: latest is not always innermost because FuncDecl.{Name,Type} overlap.):= int32(-1)for , := .indices(); < ; ++ {:= []if .index > { // push?:= .nodevar token.Posif , := .(*ast.File); {= .FileEnd// Note: files may be out of Pos order.if .FileStart > {= .index // disjoint, after; skip to next filecontinue}} else {// Edge case: FuncDecl.Name and .Type overlap:// Don't update best from Name to FuncDecl.Type.//// The condition can be read as:// - n is FuncType// - n.parent is FuncDecl// - best is strictly beneath the FuncDeclif .typ == 1<<nFuncType &&[.parent].typ == 1<<nFuncDecl &&> .parent {continue}= .End()if .Pos() > {break // disjoint, after; stop}}// Inv: node.{Pos,FileStart} <= startif <= {// node fully contains target range=// Don't search beyond end of the first match.// This is important only for an empty range (start=end)// between two adjoining nodes, which would otherwise// match both nodes; we want to match only the first.= .index} else if < {= .index // disjoint, before; skip forward}}}if >= 0 {return Cursor{.in, }, true}return Cursor{}, false}
The pages are generated with Golds v0.8.4. (GOOS=linux GOARCH=amd64)