src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

cursor.go (17471B)


      1 // Copyright 2025 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package inspector
      6 
      7 import (
      8 	"fmt"
      9 	"go/ast"
     10 	"go/token"
     11 	"iter"
     12 	"reflect"
     13 	"strings"
     14 
     15 	"golang.org/x/tools/go/ast/edge"
     16 )
     17 
     18 // A Cursor represents an [ast.Node]. It is immutable.
     19 //
     20 // Two Cursors compare equal if they represent the same node.
     21 //
     22 // The zero value of Cursor is not valid.
     23 //
     24 // Call [Inspector.Root] to obtain a cursor for the virtual root node
     25 // of the traversal. This is the sole valid cursor for which [Cursor.Node]
     26 // returns nil.
     27 //
     28 // Use the following methods to navigate efficiently around the tree:
     29 //   - for ancestors, use [Cursor.Parent] and [Cursor.Enclosing];
     30 //   - for children, use [Cursor.Child], [Cursor.Children],
     31 //     [Cursor.FirstChild], and [Cursor.LastChild];
     32 //   - for siblings, use [Cursor.PrevSibling] and [Cursor.NextSibling];
     33 //   - for descendants, use [Cursor.FindByPos], [Cursor.FindNode],
     34 //     [Cursor.Inspect], and [Cursor.Preorder].
     35 //
     36 // Use the [Cursor.ChildAt] and [Cursor.ParentEdge] methods for
     37 // information about the edges in a tree: which field (and slice
     38 // element) of the parent node holds the child.
     39 type Cursor struct {
     40 	in    *Inspector
     41 	index int32 // index of push node; -1 for virtual root node
     42 }
     43 
     44 // Root returns a valid cursor for the virtual root node,
     45 // whose children are the files provided to [New].
     46 //
     47 // Its [Cursor.Node] method return nil.
     48 func (in *Inspector) Root() Cursor {
     49 	return Cursor{in, -1}
     50 }
     51 
     52 // At returns the cursor at the specified index in the traversal,
     53 // which must have been obtained from [Cursor.Index] on a Cursor
     54 // belonging to the same Inspector (see [Cursor.Inspector]).
     55 func (in *Inspector) At(index int32) Cursor {
     56 	if index < 0 {
     57 		panic("negative index")
     58 	}
     59 	if int(index) >= len(in.events) {
     60 		panic("index out of range for this inspector")
     61 	}
     62 	if in.events[index].index < index {
     63 		panic("invalid index") // (a push, not a pop)
     64 	}
     65 	return Cursor{in, index}
     66 }
     67 
     68 // Valid reports whether the cursor is valid.
     69 // The zero value of cursor is invalid.
     70 // Unless otherwise documented, it is not safe to call
     71 // any other method on an invalid cursor.
     72 func (c Cursor) Valid() bool {
     73 	return c.in != nil
     74 }
     75 
     76 // Inspector returns the cursor's Inspector.
     77 // It returns nil if the Cursor is not valid.
     78 func (c Cursor) Inspector() *Inspector { return c.in }
     79 
     80 // Index returns the index of this cursor position within the package.
     81 //
     82 // Clients should not assume anything about the numeric Index value
     83 // except that it increases monotonically throughout the traversal.
     84 // It is provided for use with [Inspector.At].
     85 //
     86 // Index must not be called on the Root node.
     87 func (c Cursor) Index() int32 {
     88 	if c.index < 0 {
     89 		panic("Index called on Root node")
     90 	}
     91 	return c.index
     92 }
     93 
     94 // Node returns the node at the current cursor position,
     95 // or nil for the cursor returned by [Inspector.Root].
     96 func (c Cursor) Node() ast.Node {
     97 	if c.index < 0 {
     98 		return nil
     99 	}
    100 	return c.in.events[c.index].node
    101 }
    102 
    103 // String returns information about the cursor's node, if any.
    104 func (c Cursor) String() string {
    105 	if !c.Valid() {
    106 		return "(invalid)"
    107 	}
    108 	if c.index < 0 {
    109 		return "(root)"
    110 	}
    111 	return reflect.TypeOf(c.Node()).String()
    112 }
    113 
    114 // GoString returns a string describing the cursor's path from the
    115 // root, if any.
    116 func (c Cursor) GoString() string {
    117 	if !c.Valid() {
    118 		return "(invalid)"
    119 	}
    120 	if c.index < 0 {
    121 		return "(root)"
    122 	}
    123 	// e.g "File.Decls[1].(*ast.GenDecl).Specs[0].(*ast.TypeSpec)"
    124 	//
    125 	// In hindsight even the File node should have reported a
    126 	// virtual ParentEdge of (Root_Files, i) where i is the index
    127 	// among the files passed to NewInspector. Then the path would
    128 	// read "(root).Files[i]", etc; but we missed the boat.
    129 	var buf strings.Builder
    130 	buf.WriteString("File")
    131 	var visit func(Cursor)
    132 	visit = func(c Cursor) {
    133 		ek, idx := c.ParentEdge()
    134 		if ek == edge.Invalid {
    135 			return // File
    136 		}
    137 		visit(c.Parent())
    138 		fmt.Fprintf(&buf, ".%s", ek.FieldName())
    139 		if idx >= 0 {
    140 			fmt.Fprintf(&buf, "[%d]", idx)
    141 		}
    142 		ftype := ek.FieldType()
    143 		if idx >= 0 {
    144 			ftype = ftype.Elem() // []T -> T
    145 		}
    146 		if ftype.Kind() == reflect.Interface {
    147 			fmt.Fprintf(&buf, ".(%T)", c.Node())
    148 		}
    149 	}
    150 	visit(c)
    151 	return buf.String()
    152 }
    153 
    154 // indices return the [start, end) half-open interval of event indices.
    155 func (c Cursor) indices() (int32, int32) {
    156 	if c.index < 0 {
    157 		return 0, int32(len(c.in.events)) // root: all events
    158 	} else {
    159 		return c.index, c.in.events[c.index].index + 1 // just one subtree
    160 	}
    161 }
    162 
    163 // Preorder returns an iterator over the nodes of the subtree
    164 // represented by c in depth-first order. Each node in the sequence is
    165 // represented by a Cursor that allows access to the Node, but may
    166 // also be used to start a new traversal, or to obtain the stack of
    167 // nodes enclosing the cursor.
    168 //
    169 // The traversal sequence is determined by [ast.Inspect]. The types
    170 // argument, if non-empty, enables type-based filtering of events. The
    171 // function f if is called only for nodes whose type matches an
    172 // element of the types slice.
    173 //
    174 // If you need control over descent into subtrees,
    175 // or need both pre- and post-order notifications, use [Cursor.Inspect]
    176 func (c Cursor) Preorder(types ...ast.Node) iter.Seq[Cursor] {
    177 	mask := maskOf(types)
    178 
    179 	return func(yield func(Cursor) bool) {
    180 		events := c.in.events
    181 
    182 		for i, limit := c.indices(); i < limit; {
    183 			ev := events[i]
    184 			if ev.index > i { // push?
    185 				if ev.typ&mask != 0 && !yield(Cursor{c.in, i}) {
    186 					break
    187 				}
    188 				pop := ev.index
    189 				if events[pop].typ&mask == 0 {
    190 					// Subtree does not contain types: skip.
    191 					i = pop + 1
    192 					continue
    193 				}
    194 			}
    195 			i++
    196 		}
    197 	}
    198 }
    199 
    200 // Inspect visits the nodes of the subtree represented by c in
    201 // depth-first order. It calls f(n) for each node n before it
    202 // visits n's children. If f returns true, Inspect invokes f
    203 // recursively for each of the non-nil children of the node.
    204 //
    205 // Each node is represented by a Cursor that allows access to the
    206 // Node, but may also be used to start a new traversal, or to obtain
    207 // the stack of nodes enclosing the cursor.
    208 //
    209 // The complete traversal sequence is determined by [ast.Inspect].
    210 // The types argument, if non-empty, enables type-based filtering of
    211 // events. The function f if is called only for nodes whose type
    212 // matches an element of the types slice.
    213 func (c Cursor) Inspect(types []ast.Node, f func(c Cursor) (descend bool)) {
    214 	mask := maskOf(types)
    215 	events := c.in.events
    216 	for i, limit := c.indices(); i < limit; {
    217 		ev := events[i]
    218 		if ev.index > i {
    219 			// push
    220 			pop := ev.index
    221 			if ev.typ&mask != 0 && !f(Cursor{c.in, i}) ||
    222 				events[pop].typ&mask == 0 {
    223 				// The user opted not to descend, or the
    224 				// subtree does not contain types:
    225 				// skip past the pop.
    226 				i = pop + 1
    227 				continue
    228 			}
    229 		}
    230 		i++
    231 	}
    232 }
    233 
    234 // Enclosing returns an iterator over the nodes enclosing the current
    235 // current node, starting with the Cursor itself.
    236 //
    237 // Enclosing must not be called on the Root node (whose [Cursor.Node] returns nil).
    238 //
    239 // The types argument, if non-empty, enables type-based filtering of
    240 // events: the sequence includes only enclosing nodes whose type
    241 // matches an element of the types slice.
    242 func (c Cursor) Enclosing(types ...ast.Node) iter.Seq[Cursor] {
    243 	if c.index < 0 {
    244 		panic("Cursor.Enclosing called on Root node")
    245 	}
    246 
    247 	mask := maskOf(types)
    248 
    249 	return func(yield func(Cursor) bool) {
    250 		events := c.in.events
    251 		for i := c.index; i >= 0; i = events[i].parent {
    252 			if events[i].typ&mask != 0 && !yield(Cursor{c.in, i}) {
    253 				break
    254 			}
    255 		}
    256 	}
    257 }
    258 
    259 // Parent returns the parent of the current node.
    260 //
    261 // Parent must not be called on the Root node (whose [Cursor.Node] returns nil).
    262 func (c Cursor) Parent() Cursor {
    263 	if c.index < 0 {
    264 		panic("Cursor.Parent called on Root node")
    265 	}
    266 
    267 	return Cursor{c.in, c.in.events[c.index].parent}
    268 }
    269 
    270 // ParentEdge returns the identity of the field in the parent node
    271 // that holds this cursor's node, and if it is a list, the index within it.
    272 //
    273 // For example, f(x, y) is a CallExpr whose three children are Idents.
    274 // f has edge kind [edge.CallExpr_Fun] and index -1.
    275 // x and y have kind [edge.CallExpr_Args] and indices 0 and 1, respectively.
    276 //
    277 // If called on a child of the Root node, it returns ([edge.Invalid], -1).
    278 //
    279 // ParentEdge must not be called on the Root node (whose [Cursor.Node] returns nil).
    280 func (c Cursor) ParentEdge() (edge.Kind, int) {
    281 	if c.index < 0 {
    282 		panic("Cursor.ParentEdge called on Root node")
    283 	}
    284 	events := c.in.events
    285 	pop := events[c.index].index
    286 	return unpackEdgeKindAndIndex(events[pop].parent)
    287 }
    288 
    289 // ParentEdgeKind returns the kind component of the result of [Cursor.ParentEdge].
    290 func (c Cursor) ParentEdgeKind() edge.Kind {
    291 	ek, _ := c.ParentEdge()
    292 	return ek
    293 }
    294 
    295 // ParentEdgeIndex returns the index component of the result of [Cursor.ParentEdge].
    296 func (c Cursor) ParentEdgeIndex() int {
    297 	_, index := c.ParentEdge()
    298 	return index
    299 }
    300 
    301 // ChildAt returns the cursor for the child of the
    302 // current node identified by its edge and index.
    303 // The index must be -1 if the edge.Kind is not a slice.
    304 // The indicated child node must exist.
    305 //
    306 // ChildAt must not be called on the Root node (whose [Cursor.Node] returns nil).
    307 //
    308 // Invariant: c.Parent().ChildAt(c.ParentEdge()) == c.
    309 func (c Cursor) ChildAt(k edge.Kind, idx int) Cursor {
    310 	target := packEdgeKindAndIndex(k, idx)
    311 
    312 	// Unfortunately there's no shortcut to looping.
    313 	events := c.in.events
    314 	i := c.index + 1
    315 	for {
    316 		pop := events[i].index
    317 		if pop < i {
    318 			break
    319 		}
    320 		if events[pop].parent == target {
    321 			return Cursor{c.in, i}
    322 		}
    323 		i = pop + 1
    324 	}
    325 	panic(fmt.Sprintf("ChildAt(%v, %d): no such child of %v", k, idx, c))
    326 }
    327 
    328 // Child returns the cursor for n, which must be a direct child of c's Node.
    329 //
    330 // Child must not be called on the Root node (whose [Cursor.Node] returns nil).
    331 func (c Cursor) Child(n ast.Node) Cursor {
    332 	if c.index < 0 {
    333 		panic("Cursor.Child called on Root node")
    334 	}
    335 
    336 	if false {
    337 		// reference implementation
    338 		for child := range c.Children() {
    339 			if child.Node() == n {
    340 				return child
    341 			}
    342 		}
    343 
    344 	} else {
    345 		// optimized implementation
    346 		events := c.in.events
    347 		for i := c.index + 1; events[i].index > i; i = events[i].index + 1 {
    348 			if events[i].node == n {
    349 				return Cursor{c.in, i}
    350 			}
    351 		}
    352 	}
    353 	panic(fmt.Sprintf("Child(%T): not a child of %v", n, c))
    354 }
    355 
    356 // NextSibling returns the cursor for the next sibling node in the same list
    357 // (for example, of files, decls, specs, statements, fields, or expressions) as
    358 // the current node. It returns (zero, false) if the node is the last node in
    359 // the list, or is not part of a list.
    360 //
    361 // NextSibling must not be called on the Root node.
    362 //
    363 // See note at [Cursor.Children].
    364 func (c Cursor) NextSibling() (Cursor, bool) {
    365 	if c.index < 0 {
    366 		panic("Cursor.NextSibling called on Root node")
    367 	}
    368 
    369 	events := c.in.events
    370 	i := events[c.index].index + 1 // after corresponding pop
    371 	if i < int32(len(events)) {
    372 		if events[i].index > i { // push?
    373 			return Cursor{c.in, i}, true
    374 		}
    375 	}
    376 	return Cursor{}, false
    377 }
    378 
    379 // PrevSibling returns the cursor for the previous sibling node in the
    380 // same list (for example, of files, decls, specs, statements, fields,
    381 // or expressions) as the current node. It returns zero if the node is
    382 // the first node in the list, or is not part of a list.
    383 //
    384 // It must not be called on the Root node.
    385 //
    386 // See note at [Cursor.Children].
    387 func (c Cursor) PrevSibling() (Cursor, bool) {
    388 	if c.index < 0 {
    389 		panic("Cursor.PrevSibling called on Root node")
    390 	}
    391 
    392 	events := c.in.events
    393 	i := c.index - 1
    394 	if i >= 0 {
    395 		if j := events[i].index; j < i { // pop?
    396 			return Cursor{c.in, j}, true
    397 		}
    398 	}
    399 	return Cursor{}, false
    400 }
    401 
    402 // FirstChild returns the first direct child of the current node,
    403 // or zero if it has no children.
    404 func (c Cursor) FirstChild() (Cursor, bool) {
    405 	events := c.in.events
    406 	i := c.index + 1                                   // i=0 if c is root
    407 	if i < int32(len(events)) && events[i].index > i { // push?
    408 		return Cursor{c.in, i}, true
    409 	}
    410 	return Cursor{}, false
    411 }
    412 
    413 // LastChild returns the last direct child of the current node,
    414 // or zero if it has no children.
    415 func (c Cursor) LastChild() (Cursor, bool) {
    416 	events := c.in.events
    417 	if c.index < 0 { // root?
    418 		if len(events) > 0 {
    419 			// return push of final event (a pop)
    420 			return Cursor{c.in, events[len(events)-1].index}, true
    421 		}
    422 	} else {
    423 		j := events[c.index].index - 1 // before corresponding pop
    424 		// Inv: j == c.index if c has no children
    425 		//  or  j is last child's pop.
    426 		if j > c.index { // c has children
    427 			return Cursor{c.in, events[j].index}, true
    428 		}
    429 	}
    430 	return Cursor{}, false
    431 }
    432 
    433 // Children returns an iterator over the direct children of the
    434 // current node, if any.
    435 //
    436 // When using Children, NextChild, and PrevChild, bear in mind that a
    437 // Node's children may come from different fields, some of which may
    438 // be lists of nodes without a distinguished intervening container
    439 // such as [ast.BlockStmt].
    440 //
    441 // For example, [ast.CaseClause] has a field List of expressions and a
    442 // field Body of statements, so the children of a CaseClause are a mix
    443 // of expressions and statements. Other nodes that have "uncontained"
    444 // list fields include:
    445 //
    446 //   - [ast.ValueSpec] (Names, Values)
    447 //   - [ast.CompositeLit] (Type, Elts)
    448 //   - [ast.IndexListExpr] (X, Indices)
    449 //   - [ast.CallExpr] (Fun, Args)
    450 //   - [ast.AssignStmt] (Lhs, Rhs)
    451 //
    452 // So, do not assume that the previous sibling of an ast.Stmt is also
    453 // an ast.Stmt, or if it is, that they are executed sequentially,
    454 // unless you have established that, say, its parent is a BlockStmt
    455 // or its [Cursor.ParentEdge] is [edge.BlockStmt_List].
    456 // For example, given "for S1; ; S2 {}", the predecessor of S2 is S1,
    457 // even though they are not executed in sequence.
    458 func (c Cursor) Children() iter.Seq[Cursor] {
    459 	return func(yield func(Cursor) bool) {
    460 		c, ok := c.FirstChild()
    461 		for ok && yield(c) {
    462 			c, ok = c.NextSibling()
    463 		}
    464 	}
    465 }
    466 
    467 // Contains reports whether c contains or is equal to c2.
    468 //
    469 // Both Cursors must belong to the same [Inspector];
    470 // neither may be its Root node.
    471 func (c Cursor) Contains(c2 Cursor) bool {
    472 	if c.in != c2.in {
    473 		panic("different inspectors")
    474 	}
    475 	events := c.in.events
    476 	return c.index <= c2.index && events[c2.index].index <= events[c.index].index
    477 }
    478 
    479 // FindNode returns the cursor for node n if it belongs to the subtree
    480 // rooted at c. It returns zero if n is not found.
    481 func (c Cursor) FindNode(n ast.Node) (Cursor, bool) {
    482 
    483 	// FindNode is equivalent to this code,
    484 	// but more convenient and 15-20% faster:
    485 	if false {
    486 		for candidate := range c.Preorder(n) {
    487 			if candidate.Node() == n {
    488 				return candidate, true
    489 			}
    490 		}
    491 		return Cursor{}, false
    492 	}
    493 
    494 	// TODO(adonovan): opt: should we assume Node.Pos is accurate
    495 	// and combine type-based filtering with position filtering
    496 	// like FindByPos?
    497 
    498 	mask := maskOf([]ast.Node{n})
    499 	events := c.in.events
    500 
    501 	for i, limit := c.indices(); i < limit; i++ {
    502 		ev := events[i]
    503 		if ev.index > i { // push?
    504 			if ev.typ&mask != 0 && ev.node == n {
    505 				return Cursor{c.in, i}, true
    506 			}
    507 			pop := ev.index
    508 			if events[pop].typ&mask == 0 {
    509 				// Subtree does not contain type of n: skip.
    510 				i = pop
    511 			}
    512 		}
    513 	}
    514 	return Cursor{}, false
    515 }
    516 
    517 // FindByPos returns the cursor for the innermost node n in the tree
    518 // rooted at c such that n.Pos() <= start && end <= n.End().
    519 // (For an *ast.File, it uses the bounds n.FileStart-n.FileEnd.)
    520 //
    521 // An empty range (start == end) between two adjacent nodes is
    522 // considered to belong to the first node.
    523 //
    524 // It returns zero if none is found.
    525 // Precondition: start <= end.
    526 //
    527 // See also [astutil.PathEnclosingInterval], which
    528 // tolerates adjoining whitespace.
    529 func (c Cursor) FindByPos(start, end token.Pos) (Cursor, bool) {
    530 	if end < start {
    531 		panic("end < start")
    532 	}
    533 	events := c.in.events
    534 
    535 	// This algorithm could be implemented using c.Inspect,
    536 	// but it is about 2.5x slower.
    537 
    538 	// best is the push-index of the latest (=innermost) node containing range.
    539 	// (Beware: latest is not always innermost because FuncDecl.{Name,Type} overlap.)
    540 	best := int32(-1)
    541 	for i, limit := c.indices(); i < limit; i++ {
    542 		ev := events[i]
    543 		if ev.index > i { // push?
    544 			n := ev.node
    545 			var nodeEnd token.Pos
    546 			if file, ok := n.(*ast.File); ok {
    547 				nodeEnd = file.FileEnd
    548 				// Note: files may be out of Pos order.
    549 				if file.FileStart > start {
    550 					i = ev.index // disjoint, after; skip to next file
    551 					continue
    552 				}
    553 			} else {
    554 				// Edge case: FuncDecl.Name and .Type overlap:
    555 				// Don't update best from Name to FuncDecl.Type.
    556 				//
    557 				// The condition can be read as:
    558 				// - n is FuncType
    559 				// - n.parent is FuncDecl
    560 				// - best is strictly beneath the FuncDecl
    561 				if ev.typ == 1<<nFuncType &&
    562 					events[ev.parent].typ == 1<<nFuncDecl &&
    563 					best > ev.parent {
    564 					continue
    565 				}
    566 
    567 				nodeEnd = n.End()
    568 				if n.Pos() > start {
    569 					break // disjoint, after; stop
    570 				}
    571 			}
    572 
    573 			// Inv: node.{Pos,FileStart} <= start
    574 			if end <= nodeEnd {
    575 				// node fully contains target range
    576 				best = i
    577 
    578 				// Don't search beyond end of the first match.
    579 				// This is important only for an empty range (start=end)
    580 				// between two adjoining nodes, which would otherwise
    581 				// match both nodes; we want to match only the first.
    582 				limit = ev.index
    583 			} else if nodeEnd < start {
    584 				i = ev.index // disjoint, before; skip forward
    585 			}
    586 		}
    587 	}
    588 	if best >= 0 {
    589 		return Cursor{c.in, best}, true
    590 	}
    591 	return Cursor{}, false
    592 }