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 }