Initialize module and dependencies
This commit is contained in:
663
vendor/golang.org/x/tools/go/ast/astutil/enclosing.go
generated
vendored
Normal file
663
vendor/golang.org/x/tools/go/ast/astutil/enclosing.go
generated
vendored
Normal file
@@ -0,0 +1,663 @@
|
||||
// Copyright 2013 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 astutil
|
||||
|
||||
// This file defines utilities for working with source positions.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// PathEnclosingInterval returns the node that encloses the source
|
||||
// interval [start, end), and all its ancestors up to the AST root.
|
||||
//
|
||||
// The definition of "enclosing" used by this function considers
|
||||
// additional whitespace abutting a node to be enclosed by it.
|
||||
// In this example:
|
||||
//
|
||||
// z := x + y // add them
|
||||
// <-A->
|
||||
// <----B----->
|
||||
//
|
||||
// the ast.BinaryExpr(+) node is considered to enclose interval B
|
||||
// even though its [Pos()..End()) is actually only interval A.
|
||||
// This behaviour makes user interfaces more tolerant of imperfect
|
||||
// input.
|
||||
//
|
||||
// This function treats tokens as nodes, though they are not included
|
||||
// in the result. e.g. PathEnclosingInterval("+") returns the
|
||||
// enclosing ast.BinaryExpr("x + y").
|
||||
//
|
||||
// If start==end, the 1-char interval following start is used instead.
|
||||
//
|
||||
// The 'exact' result is true if the interval contains only path[0]
|
||||
// and perhaps some adjacent whitespace. It is false if the interval
|
||||
// overlaps multiple children of path[0], or if it contains only
|
||||
// interior whitespace of path[0].
|
||||
// In this example:
|
||||
//
|
||||
// z := x + y // add them
|
||||
// <--C--> <---E-->
|
||||
// ^
|
||||
// D
|
||||
//
|
||||
// intervals C, D and E are inexact. C is contained by the
|
||||
// z-assignment statement, because it spans three of its children (:=,
|
||||
// x, +). So too is the 1-char interval D, because it contains only
|
||||
// interior whitespace of the assignment. E is considered interior
|
||||
// whitespace of the BlockStmt containing the assignment.
|
||||
//
|
||||
// The resulting path is never empty; it always contains at least the
|
||||
// 'root' *ast.File. Ideally PathEnclosingInterval would reject
|
||||
// intervals that lie wholly or partially outside the range of the
|
||||
// file, but unfortunately ast.File records only the token.Pos of
|
||||
// the 'package' keyword, but not of the start of the file itself.
|
||||
func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) {
|
||||
// fmt.Printf("EnclosingInterval %d %d\n", start, end) // debugging
|
||||
|
||||
// Precondition: node.[Pos..End) and adjoining whitespace contain [start, end).
|
||||
var visit func(node ast.Node) bool
|
||||
visit = func(node ast.Node) bool {
|
||||
path = append(path, node)
|
||||
|
||||
nodePos := node.Pos()
|
||||
nodeEnd := node.End()
|
||||
|
||||
// fmt.Printf("visit(%T, %d, %d)\n", node, nodePos, nodeEnd) // debugging
|
||||
|
||||
// Intersect [start, end) with interval of node.
|
||||
if start < nodePos {
|
||||
start = nodePos
|
||||
}
|
||||
if end > nodeEnd {
|
||||
end = nodeEnd
|
||||
}
|
||||
|
||||
// Find sole child that contains [start, end).
|
||||
children := childrenOf(node)
|
||||
l := len(children)
|
||||
for i, child := range children {
|
||||
// [childPos, childEnd) is unaugmented interval of child.
|
||||
childPos := child.Pos()
|
||||
childEnd := child.End()
|
||||
|
||||
// [augPos, augEnd) is whitespace-augmented interval of child.
|
||||
augPos := childPos
|
||||
augEnd := childEnd
|
||||
if i > 0 {
|
||||
augPos = children[i-1].End() // start of preceding whitespace
|
||||
}
|
||||
if i < l-1 {
|
||||
nextChildPos := children[i+1].Pos()
|
||||
// Does [start, end) lie between child and next child?
|
||||
if start >= augEnd && end <= nextChildPos {
|
||||
return false // inexact match
|
||||
}
|
||||
augEnd = nextChildPos // end of following whitespace
|
||||
}
|
||||
|
||||
// fmt.Printf("\tchild %d: [%d..%d)\tcontains interval [%d..%d)?\n",
|
||||
// i, augPos, augEnd, start, end) // debugging
|
||||
|
||||
// Does augmented child strictly contain [start, end)?
|
||||
if augPos <= start && end <= augEnd {
|
||||
if is[tokenNode](child) {
|
||||
return true
|
||||
}
|
||||
|
||||
// childrenOf elides the FuncType node beneath FuncDecl.
|
||||
// Add it back here for TypeParams, Params, Results,
|
||||
// all FieldLists). But we don't add it back for the "func" token
|
||||
// even though it is the tree at FuncDecl.Type.Func.
|
||||
if decl, ok := node.(*ast.FuncDecl); ok {
|
||||
if fields, ok := child.(*ast.FieldList); ok && fields != decl.Recv {
|
||||
path = append(path, decl.Type)
|
||||
}
|
||||
}
|
||||
|
||||
return visit(child)
|
||||
}
|
||||
|
||||
// Does [start, end) overlap multiple children?
|
||||
// i.e. left-augmented child contains start
|
||||
// but LR-augmented child does not contain end.
|
||||
if start < childEnd && end > augEnd {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// No single child contained [start, end),
|
||||
// so node is the result. Is it exact?
|
||||
|
||||
// (It's tempting to put this condition before the
|
||||
// child loop, but it gives the wrong result in the
|
||||
// case where a node (e.g. ExprStmt) and its sole
|
||||
// child have equal intervals.)
|
||||
if start == nodePos && end == nodeEnd {
|
||||
return true // exact match
|
||||
}
|
||||
|
||||
return false // inexact: overlaps multiple children
|
||||
}
|
||||
|
||||
// Ensure [start,end) is nondecreasing.
|
||||
if start > end {
|
||||
start, end = end, start
|
||||
}
|
||||
|
||||
if start < root.End() && end > root.Pos() {
|
||||
if start == end {
|
||||
end = start + 1 // empty interval => interval of size 1
|
||||
}
|
||||
exact = visit(root)
|
||||
|
||||
// Reverse the path:
|
||||
for i, l := 0, len(path); i < l/2; i++ {
|
||||
path[i], path[l-1-i] = path[l-1-i], path[i]
|
||||
}
|
||||
} else {
|
||||
// Selection lies within whitespace preceding the
|
||||
// first (or following the last) declaration in the file.
|
||||
// The result nonetheless always includes the ast.File.
|
||||
path = append(path, root)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// tokenNode is a dummy implementation of ast.Node for a single token.
|
||||
// They are used transiently by PathEnclosingInterval but never escape
|
||||
// this package.
|
||||
type tokenNode struct {
|
||||
pos token.Pos
|
||||
end token.Pos
|
||||
}
|
||||
|
||||
func (n tokenNode) Pos() token.Pos {
|
||||
return n.pos
|
||||
}
|
||||
|
||||
func (n tokenNode) End() token.Pos {
|
||||
return n.end
|
||||
}
|
||||
|
||||
func tok(pos token.Pos, len int) ast.Node {
|
||||
return tokenNode{pos, pos + token.Pos(len)}
|
||||
}
|
||||
|
||||
// childrenOf returns the direct non-nil children of ast.Node n.
|
||||
// It may include fake ast.Node implementations for bare tokens.
|
||||
// it is not safe to call (e.g.) ast.Walk on such nodes.
|
||||
func childrenOf(n ast.Node) []ast.Node {
|
||||
var children []ast.Node
|
||||
|
||||
// First add nodes for all true subtrees.
|
||||
ast.Inspect(n, func(node ast.Node) bool {
|
||||
if node == n { // push n
|
||||
return true // recur
|
||||
}
|
||||
if node != nil { // push child
|
||||
children = append(children, node)
|
||||
}
|
||||
return false // no recursion
|
||||
})
|
||||
|
||||
// TODO(adonovan): be more careful about missing (!Pos.Valid)
|
||||
// tokens in trees produced from invalid input.
|
||||
|
||||
// Then add fake Nodes for bare tokens.
|
||||
switch n := n.(type) {
|
||||
case *ast.ArrayType:
|
||||
children = append(children,
|
||||
tok(n.Lbrack, len("[")),
|
||||
tok(n.Elt.End(), len("]")))
|
||||
|
||||
case *ast.AssignStmt:
|
||||
children = append(children,
|
||||
tok(n.TokPos, len(n.Tok.String())))
|
||||
|
||||
case *ast.BasicLit:
|
||||
children = append(children,
|
||||
tok(n.ValuePos, len(n.Value)))
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
children = append(children, tok(n.OpPos, len(n.Op.String())))
|
||||
|
||||
case *ast.BlockStmt:
|
||||
if n.Lbrace.IsValid() {
|
||||
children = append(children, tok(n.Lbrace, len("{")))
|
||||
}
|
||||
if n.Rbrace.IsValid() {
|
||||
children = append(children, tok(n.Rbrace, len("}")))
|
||||
}
|
||||
|
||||
case *ast.BranchStmt:
|
||||
children = append(children,
|
||||
tok(n.TokPos, len(n.Tok.String())))
|
||||
|
||||
case *ast.CallExpr:
|
||||
children = append(children,
|
||||
tok(n.Lparen, len("(")),
|
||||
tok(n.Rparen, len(")")))
|
||||
if n.Ellipsis != 0 {
|
||||
children = append(children, tok(n.Ellipsis, len("...")))
|
||||
}
|
||||
|
||||
case *ast.CaseClause:
|
||||
if n.List == nil {
|
||||
children = append(children,
|
||||
tok(n.Case, len("default")))
|
||||
} else {
|
||||
children = append(children,
|
||||
tok(n.Case, len("case")))
|
||||
}
|
||||
children = append(children, tok(n.Colon, len(":")))
|
||||
|
||||
case *ast.ChanType:
|
||||
switch n.Dir {
|
||||
case ast.RECV:
|
||||
children = append(children, tok(n.Begin, len("<-chan")))
|
||||
case ast.SEND:
|
||||
children = append(children, tok(n.Begin, len("chan<-")))
|
||||
case ast.RECV | ast.SEND:
|
||||
children = append(children, tok(n.Begin, len("chan")))
|
||||
}
|
||||
|
||||
case *ast.CommClause:
|
||||
if n.Comm == nil {
|
||||
children = append(children,
|
||||
tok(n.Case, len("default")))
|
||||
} else {
|
||||
children = append(children,
|
||||
tok(n.Case, len("case")))
|
||||
}
|
||||
children = append(children, tok(n.Colon, len(":")))
|
||||
|
||||
case *ast.Comment:
|
||||
// nop
|
||||
|
||||
case *ast.CommentGroup:
|
||||
// nop
|
||||
|
||||
case *ast.CompositeLit:
|
||||
children = append(children,
|
||||
tok(n.Lbrace, len("{")),
|
||||
tok(n.Rbrace, len("{")))
|
||||
|
||||
case *ast.DeclStmt:
|
||||
// nop
|
||||
|
||||
case *ast.DeferStmt:
|
||||
children = append(children,
|
||||
tok(n.Defer, len("defer")))
|
||||
|
||||
case *ast.Ellipsis:
|
||||
children = append(children,
|
||||
tok(n.Ellipsis, len("...")))
|
||||
|
||||
case *ast.EmptyStmt:
|
||||
// nop
|
||||
|
||||
case *ast.ExprStmt:
|
||||
// nop
|
||||
|
||||
case *ast.Field:
|
||||
// TODO(adonovan): Field.{Doc,Comment,Tag}?
|
||||
|
||||
case *ast.FieldList:
|
||||
if n.Opening.IsValid() {
|
||||
children = append(children, tok(n.Opening, len("(")))
|
||||
}
|
||||
if n.Closing.IsValid() {
|
||||
children = append(children, tok(n.Closing, len(")")))
|
||||
}
|
||||
|
||||
case *ast.File:
|
||||
// TODO test: Doc
|
||||
children = append(children,
|
||||
tok(n.Package, len("package")))
|
||||
|
||||
case *ast.ForStmt:
|
||||
children = append(children,
|
||||
tok(n.For, len("for")))
|
||||
|
||||
case *ast.FuncDecl:
|
||||
// TODO(adonovan): FuncDecl.Comment?
|
||||
|
||||
// Uniquely, FuncDecl breaks the invariant that
|
||||
// preorder traversal yields tokens in lexical order:
|
||||
// in fact, FuncDecl.Recv precedes FuncDecl.Type.Func.
|
||||
//
|
||||
// As a workaround, we inline the case for FuncType
|
||||
// here and order things correctly.
|
||||
// We also need to insert the elided FuncType just
|
||||
// before the 'visit' recursion.
|
||||
//
|
||||
children = nil // discard ast.Walk(FuncDecl) info subtrees
|
||||
children = append(children, tok(n.Type.Func, len("func")))
|
||||
if n.Recv != nil {
|
||||
children = append(children, n.Recv)
|
||||
}
|
||||
children = append(children, n.Name)
|
||||
if tparams := n.Type.TypeParams; tparams != nil {
|
||||
children = append(children, tparams)
|
||||
}
|
||||
if n.Type.Params != nil {
|
||||
children = append(children, n.Type.Params)
|
||||
}
|
||||
if n.Type.Results != nil {
|
||||
children = append(children, n.Type.Results)
|
||||
}
|
||||
if n.Body != nil {
|
||||
children = append(children, n.Body)
|
||||
}
|
||||
|
||||
case *ast.FuncLit:
|
||||
// nop
|
||||
|
||||
case *ast.FuncType:
|
||||
if n.Func != 0 {
|
||||
children = append(children,
|
||||
tok(n.Func, len("func")))
|
||||
}
|
||||
|
||||
case *ast.GenDecl:
|
||||
children = append(children,
|
||||
tok(n.TokPos, len(n.Tok.String())))
|
||||
if n.Lparen != 0 {
|
||||
children = append(children,
|
||||
tok(n.Lparen, len("(")),
|
||||
tok(n.Rparen, len(")")))
|
||||
}
|
||||
|
||||
case *ast.GoStmt:
|
||||
children = append(children,
|
||||
tok(n.Go, len("go")))
|
||||
|
||||
case *ast.Ident:
|
||||
children = append(children,
|
||||
tok(n.NamePos, len(n.Name)))
|
||||
|
||||
case *ast.IfStmt:
|
||||
children = append(children,
|
||||
tok(n.If, len("if")))
|
||||
|
||||
case *ast.ImportSpec:
|
||||
// TODO(adonovan): ImportSpec.{Doc,EndPos}?
|
||||
|
||||
case *ast.IncDecStmt:
|
||||
children = append(children,
|
||||
tok(n.TokPos, len(n.Tok.String())))
|
||||
|
||||
case *ast.IndexExpr:
|
||||
children = append(children,
|
||||
tok(n.Lbrack, len("[")),
|
||||
tok(n.Rbrack, len("]")))
|
||||
|
||||
case *ast.IndexListExpr:
|
||||
children = append(children,
|
||||
tok(n.Lbrack, len("[")),
|
||||
tok(n.Rbrack, len("]")))
|
||||
|
||||
case *ast.InterfaceType:
|
||||
children = append(children,
|
||||
tok(n.Interface, len("interface")))
|
||||
|
||||
case *ast.KeyValueExpr:
|
||||
children = append(children,
|
||||
tok(n.Colon, len(":")))
|
||||
|
||||
case *ast.LabeledStmt:
|
||||
children = append(children,
|
||||
tok(n.Colon, len(":")))
|
||||
|
||||
case *ast.MapType:
|
||||
children = append(children,
|
||||
tok(n.Map, len("map")))
|
||||
|
||||
case *ast.ParenExpr:
|
||||
children = append(children,
|
||||
tok(n.Lparen, len("(")),
|
||||
tok(n.Rparen, len(")")))
|
||||
|
||||
case *ast.RangeStmt:
|
||||
children = append(children,
|
||||
tok(n.For, len("for")),
|
||||
tok(n.TokPos, len(n.Tok.String())))
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
children = append(children,
|
||||
tok(n.Return, len("return")))
|
||||
|
||||
case *ast.SelectStmt:
|
||||
children = append(children,
|
||||
tok(n.Select, len("select")))
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
// nop
|
||||
|
||||
case *ast.SendStmt:
|
||||
children = append(children,
|
||||
tok(n.Arrow, len("<-")))
|
||||
|
||||
case *ast.SliceExpr:
|
||||
children = append(children,
|
||||
tok(n.Lbrack, len("[")),
|
||||
tok(n.Rbrack, len("]")))
|
||||
|
||||
case *ast.StarExpr:
|
||||
children = append(children, tok(n.Star, len("*")))
|
||||
|
||||
case *ast.StructType:
|
||||
children = append(children, tok(n.Struct, len("struct")))
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
children = append(children, tok(n.Switch, len("switch")))
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
children = append(children,
|
||||
tok(n.Lparen-1, len(".")),
|
||||
tok(n.Lparen, len("(")),
|
||||
tok(n.Rparen, len(")")))
|
||||
|
||||
case *ast.TypeSpec:
|
||||
// TODO(adonovan): TypeSpec.{Doc,Comment}?
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
children = append(children, tok(n.Switch, len("switch")))
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
children = append(children, tok(n.OpPos, len(n.Op.String())))
|
||||
|
||||
case *ast.ValueSpec:
|
||||
// TODO(adonovan): ValueSpec.{Doc,Comment}?
|
||||
|
||||
case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt:
|
||||
// nop
|
||||
}
|
||||
|
||||
// TODO(adonovan): opt: merge the logic of ast.Inspect() into
|
||||
// the switch above so we can make interleaved callbacks for
|
||||
// both Nodes and Tokens in the right order and avoid the need
|
||||
// to sort.
|
||||
sort.Sort(byPos(children))
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
type byPos []ast.Node
|
||||
|
||||
func (sl byPos) Len() int {
|
||||
return len(sl)
|
||||
}
|
||||
func (sl byPos) Less(i, j int) bool {
|
||||
return sl[i].Pos() < sl[j].Pos()
|
||||
}
|
||||
func (sl byPos) Swap(i, j int) {
|
||||
sl[i], sl[j] = sl[j], sl[i]
|
||||
}
|
||||
|
||||
// NodeDescription returns a description of the concrete type of n suitable
|
||||
// for a user interface.
|
||||
//
|
||||
// TODO(adonovan): in some cases (e.g. Field, FieldList, Ident,
|
||||
// StarExpr) we could be much more specific given the path to the AST
|
||||
// root. Perhaps we should do that.
|
||||
func NodeDescription(n ast.Node) string {
|
||||
switch n := n.(type) {
|
||||
case *ast.ArrayType:
|
||||
return "array type"
|
||||
case *ast.AssignStmt:
|
||||
return "assignment"
|
||||
case *ast.BadDecl:
|
||||
return "bad declaration"
|
||||
case *ast.BadExpr:
|
||||
return "bad expression"
|
||||
case *ast.BadStmt:
|
||||
return "bad statement"
|
||||
case *ast.BasicLit:
|
||||
return "basic literal"
|
||||
case *ast.BinaryExpr:
|
||||
return fmt.Sprintf("binary %s operation", n.Op)
|
||||
case *ast.BlockStmt:
|
||||
return "block"
|
||||
case *ast.BranchStmt:
|
||||
switch n.Tok {
|
||||
case token.BREAK:
|
||||
return "break statement"
|
||||
case token.CONTINUE:
|
||||
return "continue statement"
|
||||
case token.GOTO:
|
||||
return "goto statement"
|
||||
case token.FALLTHROUGH:
|
||||
return "fall-through statement"
|
||||
}
|
||||
case *ast.CallExpr:
|
||||
if len(n.Args) == 1 && !n.Ellipsis.IsValid() {
|
||||
return "function call (or conversion)"
|
||||
}
|
||||
return "function call"
|
||||
case *ast.CaseClause:
|
||||
return "case clause"
|
||||
case *ast.ChanType:
|
||||
return "channel type"
|
||||
case *ast.CommClause:
|
||||
return "communication clause"
|
||||
case *ast.Comment:
|
||||
return "comment"
|
||||
case *ast.CommentGroup:
|
||||
return "comment group"
|
||||
case *ast.CompositeLit:
|
||||
return "composite literal"
|
||||
case *ast.DeclStmt:
|
||||
return NodeDescription(n.Decl) + " statement"
|
||||
case *ast.DeferStmt:
|
||||
return "defer statement"
|
||||
case *ast.Ellipsis:
|
||||
return "ellipsis"
|
||||
case *ast.EmptyStmt:
|
||||
return "empty statement"
|
||||
case *ast.ExprStmt:
|
||||
return "expression statement"
|
||||
case *ast.Field:
|
||||
// Can be any of these:
|
||||
// struct {x, y int} -- struct field(s)
|
||||
// struct {T} -- anon struct field
|
||||
// interface {I} -- interface embedding
|
||||
// interface {f()} -- interface method
|
||||
// func (A) func(B) C -- receiver, param(s), result(s)
|
||||
return "field/method/parameter"
|
||||
case *ast.FieldList:
|
||||
return "field/method/parameter list"
|
||||
case *ast.File:
|
||||
return "source file"
|
||||
case *ast.ForStmt:
|
||||
return "for loop"
|
||||
case *ast.FuncDecl:
|
||||
return "function declaration"
|
||||
case *ast.FuncLit:
|
||||
return "function literal"
|
||||
case *ast.FuncType:
|
||||
return "function type"
|
||||
case *ast.GenDecl:
|
||||
switch n.Tok {
|
||||
case token.IMPORT:
|
||||
return "import declaration"
|
||||
case token.CONST:
|
||||
return "constant declaration"
|
||||
case token.TYPE:
|
||||
return "type declaration"
|
||||
case token.VAR:
|
||||
return "variable declaration"
|
||||
}
|
||||
case *ast.GoStmt:
|
||||
return "go statement"
|
||||
case *ast.Ident:
|
||||
return "identifier"
|
||||
case *ast.IfStmt:
|
||||
return "if statement"
|
||||
case *ast.ImportSpec:
|
||||
return "import specification"
|
||||
case *ast.IncDecStmt:
|
||||
if n.Tok == token.INC {
|
||||
return "increment statement"
|
||||
}
|
||||
return "decrement statement"
|
||||
case *ast.IndexExpr:
|
||||
return "index expression"
|
||||
case *ast.IndexListExpr:
|
||||
return "index list expression"
|
||||
case *ast.InterfaceType:
|
||||
return "interface type"
|
||||
case *ast.KeyValueExpr:
|
||||
return "key/value association"
|
||||
case *ast.LabeledStmt:
|
||||
return "statement label"
|
||||
case *ast.MapType:
|
||||
return "map type"
|
||||
case *ast.Package:
|
||||
return "package"
|
||||
case *ast.ParenExpr:
|
||||
return "parenthesized " + NodeDescription(n.X)
|
||||
case *ast.RangeStmt:
|
||||
return "range loop"
|
||||
case *ast.ReturnStmt:
|
||||
return "return statement"
|
||||
case *ast.SelectStmt:
|
||||
return "select statement"
|
||||
case *ast.SelectorExpr:
|
||||
return "selector"
|
||||
case *ast.SendStmt:
|
||||
return "channel send"
|
||||
case *ast.SliceExpr:
|
||||
return "slice expression"
|
||||
case *ast.StarExpr:
|
||||
return "*-operation" // load/store expr or pointer type
|
||||
case *ast.StructType:
|
||||
return "struct type"
|
||||
case *ast.SwitchStmt:
|
||||
return "switch statement"
|
||||
case *ast.TypeAssertExpr:
|
||||
return "type assertion"
|
||||
case *ast.TypeSpec:
|
||||
return "type specification"
|
||||
case *ast.TypeSwitchStmt:
|
||||
return "type switch"
|
||||
case *ast.UnaryExpr:
|
||||
return fmt.Sprintf("unary %s operation", n.Op)
|
||||
case *ast.ValueSpec:
|
||||
return "value specification"
|
||||
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected node type: %T", n))
|
||||
}
|
||||
|
||||
func is[T any](x any) bool {
|
||||
_, ok := x.(T)
|
||||
return ok
|
||||
}
|
||||
487
vendor/golang.org/x/tools/go/ast/astutil/imports.go
generated
vendored
Normal file
487
vendor/golang.org/x/tools/go/ast/astutil/imports.go
generated
vendored
Normal file
@@ -0,0 +1,487 @@
|
||||
// Copyright 2013 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 astutil contains common utilities for working with the Go AST.
|
||||
package astutil // import "golang.org/x/tools/go/ast/astutil"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AddImport adds the import path to the file f, if absent.
|
||||
func AddImport(fset *token.FileSet, f *ast.File, path string) (added bool) {
|
||||
return AddNamedImport(fset, f, "", path)
|
||||
}
|
||||
|
||||
// AddNamedImport adds the import with the given name and path to the file f, if absent.
|
||||
// If name is not empty, it is used to rename the import.
|
||||
//
|
||||
// For example, calling
|
||||
//
|
||||
// AddNamedImport(fset, f, "pathpkg", "path")
|
||||
//
|
||||
// adds
|
||||
//
|
||||
// import pathpkg "path"
|
||||
func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added bool) {
|
||||
if imports(f, name, path) {
|
||||
return false
|
||||
}
|
||||
|
||||
newImport := &ast.ImportSpec{
|
||||
Path: &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: strconv.Quote(path),
|
||||
},
|
||||
}
|
||||
if name != "" {
|
||||
newImport.Name = &ast.Ident{Name: name}
|
||||
}
|
||||
|
||||
// Find an import decl to add to.
|
||||
// The goal is to find an existing import
|
||||
// whose import path has the longest shared
|
||||
// prefix with path.
|
||||
var (
|
||||
bestMatch = -1 // length of longest shared prefix
|
||||
lastImport = -1 // index in f.Decls of the file's final import decl
|
||||
impDecl *ast.GenDecl // import decl containing the best match
|
||||
impIndex = -1 // spec index in impDecl containing the best match
|
||||
|
||||
isThirdPartyPath = isThirdParty(path)
|
||||
)
|
||||
for i, decl := range f.Decls {
|
||||
gen, ok := decl.(*ast.GenDecl)
|
||||
if ok && gen.Tok == token.IMPORT {
|
||||
lastImport = i
|
||||
// Do not add to import "C", to avoid disrupting the
|
||||
// association with its doc comment, breaking cgo.
|
||||
if declImports(gen, "C") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Match an empty import decl if that's all that is available.
|
||||
if len(gen.Specs) == 0 && bestMatch == -1 {
|
||||
impDecl = gen
|
||||
}
|
||||
|
||||
// Compute longest shared prefix with imports in this group and find best
|
||||
// matched import spec.
|
||||
// 1. Always prefer import spec with longest shared prefix.
|
||||
// 2. While match length is 0,
|
||||
// - for stdlib package: prefer first import spec.
|
||||
// - for third party package: prefer first third party import spec.
|
||||
// We cannot use last import spec as best match for third party package
|
||||
// because grouped imports are usually placed last by goimports -local
|
||||
// flag.
|
||||
// See issue #19190.
|
||||
seenAnyThirdParty := false
|
||||
for j, spec := range gen.Specs {
|
||||
impspec := spec.(*ast.ImportSpec)
|
||||
p := importPath(impspec)
|
||||
n := matchLen(p, path)
|
||||
if n > bestMatch || (bestMatch == 0 && !seenAnyThirdParty && isThirdPartyPath) {
|
||||
bestMatch = n
|
||||
impDecl = gen
|
||||
impIndex = j
|
||||
}
|
||||
seenAnyThirdParty = seenAnyThirdParty || isThirdParty(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no import decl found, add one after the last import.
|
||||
if impDecl == nil {
|
||||
impDecl = &ast.GenDecl{
|
||||
Tok: token.IMPORT,
|
||||
}
|
||||
if lastImport >= 0 {
|
||||
impDecl.TokPos = f.Decls[lastImport].End()
|
||||
} else {
|
||||
// There are no existing imports.
|
||||
// Our new import, preceded by a blank line, goes after the package declaration
|
||||
// and after the comment, if any, that starts on the same line as the
|
||||
// package declaration.
|
||||
impDecl.TokPos = f.Package
|
||||
|
||||
file := fset.File(f.Package)
|
||||
pkgLine := file.Line(f.Package)
|
||||
for _, c := range f.Comments {
|
||||
if file.Line(c.Pos()) > pkgLine {
|
||||
break
|
||||
}
|
||||
// +2 for a blank line
|
||||
impDecl.TokPos = c.End() + 2
|
||||
}
|
||||
}
|
||||
f.Decls = append(f.Decls, nil)
|
||||
copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:])
|
||||
f.Decls[lastImport+1] = impDecl
|
||||
}
|
||||
|
||||
// Insert new import at insertAt.
|
||||
insertAt := 0
|
||||
if impIndex >= 0 {
|
||||
// insert after the found import
|
||||
insertAt = impIndex + 1
|
||||
}
|
||||
impDecl.Specs = append(impDecl.Specs, nil)
|
||||
copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:])
|
||||
impDecl.Specs[insertAt] = newImport
|
||||
pos := impDecl.Pos()
|
||||
if insertAt > 0 {
|
||||
// If there is a comment after an existing import, preserve the comment
|
||||
// position by adding the new import after the comment.
|
||||
if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil {
|
||||
pos = spec.Comment.End()
|
||||
} else {
|
||||
// Assign same position as the previous import,
|
||||
// so that the sorter sees it as being in the same block.
|
||||
pos = impDecl.Specs[insertAt-1].Pos()
|
||||
}
|
||||
}
|
||||
if newImport.Name != nil {
|
||||
newImport.Name.NamePos = pos
|
||||
}
|
||||
updateBasicLitPos(newImport.Path, pos)
|
||||
newImport.EndPos = pos
|
||||
|
||||
// Clean up parens. impDecl contains at least one spec.
|
||||
if len(impDecl.Specs) == 1 {
|
||||
// Remove unneeded parens.
|
||||
impDecl.Lparen = token.NoPos
|
||||
} else if !impDecl.Lparen.IsValid() {
|
||||
// impDecl needs parens added.
|
||||
impDecl.Lparen = impDecl.Specs[0].Pos()
|
||||
}
|
||||
|
||||
f.Imports = append(f.Imports, newImport)
|
||||
|
||||
if len(f.Decls) <= 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Merge all the import declarations into the first one.
|
||||
var first *ast.GenDecl
|
||||
for i := 0; i < len(f.Decls); i++ {
|
||||
decl := f.Decls[i]
|
||||
gen, ok := decl.(*ast.GenDecl)
|
||||
if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") {
|
||||
continue
|
||||
}
|
||||
if first == nil {
|
||||
first = gen
|
||||
continue // Don't touch the first one.
|
||||
}
|
||||
// We now know there is more than one package in this import
|
||||
// declaration. Ensure that it ends up parenthesized.
|
||||
first.Lparen = first.Pos()
|
||||
// Move the imports of the other import declaration to the first one.
|
||||
for _, spec := range gen.Specs {
|
||||
updateBasicLitPos(spec.(*ast.ImportSpec).Path, first.Pos())
|
||||
first.Specs = append(first.Specs, spec)
|
||||
}
|
||||
f.Decls = slices.Delete(f.Decls, i, i+1)
|
||||
i--
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isThirdParty(importPath string) bool {
|
||||
// Third party package import path usually contains "." (".com", ".org", ...)
|
||||
// This logic is taken from golang.org/x/tools/imports package.
|
||||
return strings.Contains(importPath, ".")
|
||||
}
|
||||
|
||||
// DeleteImport deletes the import path from the file f, if present.
|
||||
// If there are duplicate import declarations, all matching ones are deleted.
|
||||
func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) {
|
||||
return DeleteNamedImport(fset, f, "", path)
|
||||
}
|
||||
|
||||
// DeleteNamedImport deletes the import with the given name and path from the file f, if present.
|
||||
// If there are duplicate import declarations, all matching ones are deleted.
|
||||
func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) {
|
||||
var (
|
||||
delspecs = make(map[*ast.ImportSpec]bool)
|
||||
delcomments = make(map[*ast.CommentGroup]bool)
|
||||
)
|
||||
|
||||
// Find the import nodes that import path, if any.
|
||||
for i := 0; i < len(f.Decls); i++ {
|
||||
gen, ok := f.Decls[i].(*ast.GenDecl)
|
||||
if !ok || gen.Tok != token.IMPORT {
|
||||
continue
|
||||
}
|
||||
for j := 0; j < len(gen.Specs); j++ {
|
||||
impspec := gen.Specs[j].(*ast.ImportSpec)
|
||||
if importName(impspec) != name || importPath(impspec) != path {
|
||||
continue
|
||||
}
|
||||
|
||||
// We found an import spec that imports path.
|
||||
// Delete it.
|
||||
delspecs[impspec] = true
|
||||
deleted = true
|
||||
gen.Specs = slices.Delete(gen.Specs, j, j+1)
|
||||
|
||||
// If this was the last import spec in this decl,
|
||||
// delete the decl, too.
|
||||
if len(gen.Specs) == 0 {
|
||||
f.Decls = slices.Delete(f.Decls, i, i+1)
|
||||
i--
|
||||
break
|
||||
} else if len(gen.Specs) == 1 {
|
||||
if impspec.Doc != nil {
|
||||
delcomments[impspec.Doc] = true
|
||||
}
|
||||
if impspec.Comment != nil {
|
||||
delcomments[impspec.Comment] = true
|
||||
}
|
||||
for _, cg := range f.Comments {
|
||||
// Found comment on the same line as the import spec.
|
||||
if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line {
|
||||
delcomments[cg] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
spec := gen.Specs[0].(*ast.ImportSpec)
|
||||
|
||||
// Move the documentation right after the import decl.
|
||||
if spec.Doc != nil {
|
||||
for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Doc.Pos()).Line {
|
||||
fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
|
||||
}
|
||||
}
|
||||
for _, cg := range f.Comments {
|
||||
if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line {
|
||||
for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Pos()).Line {
|
||||
fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if j > 0 {
|
||||
lastImpspec := gen.Specs[j-1].(*ast.ImportSpec)
|
||||
lastLine := fset.PositionFor(lastImpspec.Path.ValuePos, false).Line
|
||||
line := fset.PositionFor(impspec.Path.ValuePos, false).Line
|
||||
|
||||
// We deleted an entry but now there may be
|
||||
// a blank line-sized hole where the import was.
|
||||
if line-lastLine > 1 || !gen.Rparen.IsValid() {
|
||||
// There was a blank line immediately preceding the deleted import,
|
||||
// so there's no need to close the hole. The right parenthesis is
|
||||
// invalid after AddImport to an import statement without parenthesis.
|
||||
// Do nothing.
|
||||
} else if line != fset.File(gen.Rparen).LineCount() {
|
||||
// There was no blank line. Close the hole.
|
||||
fset.File(gen.Rparen).MergeLine(line)
|
||||
}
|
||||
}
|
||||
j--
|
||||
}
|
||||
}
|
||||
|
||||
// Delete imports from f.Imports.
|
||||
before := len(f.Imports)
|
||||
f.Imports = slices.DeleteFunc(f.Imports, func(imp *ast.ImportSpec) bool {
|
||||
_, ok := delspecs[imp]
|
||||
return ok
|
||||
})
|
||||
if len(f.Imports)+len(delspecs) != before {
|
||||
// This can happen when the AST is invalid (i.e. imports differ between f.Decls and f.Imports).
|
||||
panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs))
|
||||
}
|
||||
|
||||
// Delete comments from f.Comments.
|
||||
f.Comments = slices.DeleteFunc(f.Comments, func(cg *ast.CommentGroup) bool {
|
||||
_, ok := delcomments[cg]
|
||||
return ok
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// RewriteImport rewrites any import of path oldPath to path newPath.
|
||||
func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) {
|
||||
for _, imp := range f.Imports {
|
||||
if importPath(imp) == oldPath {
|
||||
rewrote = true
|
||||
// record old End, because the default is to compute
|
||||
// it using the length of imp.Path.Value.
|
||||
imp.EndPos = imp.End()
|
||||
imp.Path.Value = strconv.Quote(newPath)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UsesImport reports whether a given import is used.
|
||||
// The provided File must have been parsed with syntactic object resolution
|
||||
// (not using go/parser.SkipObjectResolution).
|
||||
func UsesImport(f *ast.File, path string) (used bool) {
|
||||
if f.Scope == nil {
|
||||
panic("file f was not parsed with syntactic object resolution")
|
||||
}
|
||||
spec := importSpec(f, path)
|
||||
if spec == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := spec.Name.String()
|
||||
switch name {
|
||||
case "<nil>":
|
||||
// If the package name is not explicitly specified,
|
||||
// make an educated guess. This is not guaranteed to be correct.
|
||||
lastSlash := strings.LastIndex(path, "/")
|
||||
if lastSlash == -1 {
|
||||
name = path
|
||||
} else {
|
||||
name = path[lastSlash+1:]
|
||||
}
|
||||
case "_", ".":
|
||||
// Not sure if this import is used - err on the side of caution.
|
||||
return true
|
||||
}
|
||||
|
||||
ast.Walk(visitFn(func(n ast.Node) {
|
||||
sel, ok := n.(*ast.SelectorExpr)
|
||||
if ok && isTopName(sel.X, name) {
|
||||
used = true
|
||||
}
|
||||
}), f)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type visitFn func(node ast.Node)
|
||||
|
||||
func (fn visitFn) Visit(node ast.Node) ast.Visitor {
|
||||
fn(node)
|
||||
return fn
|
||||
}
|
||||
|
||||
// imports reports whether f has an import with the specified name and path.
|
||||
func imports(f *ast.File, name, path string) bool {
|
||||
for _, s := range f.Imports {
|
||||
if importName(s) == name && importPath(s) == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// importSpec returns the import spec if f imports path,
|
||||
// or nil otherwise.
|
||||
func importSpec(f *ast.File, path string) *ast.ImportSpec {
|
||||
for _, s := range f.Imports {
|
||||
if importPath(s) == path {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// importName returns the name of s,
|
||||
// or "" if the import is not named.
|
||||
func importName(s *ast.ImportSpec) string {
|
||||
if s.Name == nil {
|
||||
return ""
|
||||
}
|
||||
return s.Name.Name
|
||||
}
|
||||
|
||||
// importPath returns the unquoted import path of s,
|
||||
// or "" if the path is not properly quoted.
|
||||
func importPath(s *ast.ImportSpec) string {
|
||||
t, err := strconv.Unquote(s.Path.Value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// declImports reports whether gen contains an import of path.
|
||||
func declImports(gen *ast.GenDecl, path string) bool {
|
||||
if gen.Tok != token.IMPORT {
|
||||
return false
|
||||
}
|
||||
for _, spec := range gen.Specs {
|
||||
impspec := spec.(*ast.ImportSpec)
|
||||
if importPath(impspec) == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// matchLen returns the length of the longest path segment prefix shared by x and y.
|
||||
func matchLen(x, y string) int {
|
||||
n := 0
|
||||
for i := 0; i < len(x) && i < len(y) && x[i] == y[i]; i++ {
|
||||
if x[i] == '/' {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// isTopName returns true if n is a top-level unresolved identifier with the given name.
|
||||
func isTopName(n ast.Expr, name string) bool {
|
||||
id, ok := n.(*ast.Ident)
|
||||
return ok && id.Name == name && id.Obj == nil
|
||||
}
|
||||
|
||||
// Imports returns the file imports grouped by paragraph.
|
||||
func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec {
|
||||
var groups [][]*ast.ImportSpec
|
||||
|
||||
for _, decl := range f.Decls {
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok || genDecl.Tok != token.IMPORT {
|
||||
break
|
||||
}
|
||||
|
||||
group := []*ast.ImportSpec{}
|
||||
|
||||
var lastLine int
|
||||
for _, spec := range genDecl.Specs {
|
||||
importSpec := spec.(*ast.ImportSpec)
|
||||
pos := importSpec.Path.ValuePos
|
||||
line := fset.Position(pos).Line
|
||||
if lastLine > 0 && pos > 0 && line-lastLine > 1 {
|
||||
groups = append(groups, group)
|
||||
group = []*ast.ImportSpec{}
|
||||
}
|
||||
group = append(group, importSpec)
|
||||
lastLine = line
|
||||
}
|
||||
groups = append(groups, group)
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
// updateBasicLitPos updates lit.Pos,
|
||||
// ensuring that lit.End (if set) is displaced by the same amount.
|
||||
// (See https://go.dev/issue/76395.)
|
||||
func updateBasicLitPos(lit *ast.BasicLit, pos token.Pos) {
|
||||
len := lit.End() - lit.Pos()
|
||||
lit.ValuePos = pos
|
||||
// TODO(adonovan): after go1.26, simplify to:
|
||||
// lit.ValueEnd = pos + len
|
||||
v := reflect.ValueOf(lit).Elem().FieldByName("ValueEnd")
|
||||
if v.IsValid() && v.Int() != 0 {
|
||||
v.SetInt(int64(pos + len))
|
||||
}
|
||||
}
|
||||
490
vendor/golang.org/x/tools/go/ast/astutil/rewrite.go
generated
vendored
Normal file
490
vendor/golang.org/x/tools/go/ast/astutil/rewrite.go
generated
vendored
Normal file
@@ -0,0 +1,490 @@
|
||||
// Copyright 2017 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 astutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// An ApplyFunc is invoked by Apply for each node n, even if n is nil,
|
||||
// before and/or after the node's children, using a Cursor describing
|
||||
// the current node and providing operations on it.
|
||||
//
|
||||
// The return value of ApplyFunc controls the syntax tree traversal.
|
||||
// See Apply for details.
|
||||
type ApplyFunc func(*Cursor) bool
|
||||
|
||||
// Apply traverses a syntax tree recursively, starting with root,
|
||||
// and calling pre and post for each node as described below.
|
||||
// Apply returns the syntax tree, possibly modified.
|
||||
//
|
||||
// If pre is not nil, it is called for each node before the node's
|
||||
// children are traversed (pre-order). If pre returns false, no
|
||||
// children are traversed, and post is not called for that node.
|
||||
//
|
||||
// If post is not nil, and a prior call of pre didn't return false,
|
||||
// post is called for each node after its children are traversed
|
||||
// (post-order). If post returns false, traversal is terminated and
|
||||
// Apply returns immediately.
|
||||
//
|
||||
// Only fields that refer to AST nodes are considered children;
|
||||
// i.e., token.Pos, Scopes, Objects, and fields of basic types
|
||||
// (strings, etc.) are ignored.
|
||||
//
|
||||
// Children are traversed in the order in which they appear in the
|
||||
// respective node's struct definition. A package's files are
|
||||
// traversed in the filenames' alphabetical order.
|
||||
func Apply(root ast.Node, pre, post ApplyFunc) (result ast.Node) {
|
||||
parent := &struct{ ast.Node }{root}
|
||||
defer func() {
|
||||
if r := recover(); r != nil && r != abort {
|
||||
panic(r)
|
||||
}
|
||||
result = parent.Node
|
||||
}()
|
||||
a := &application{pre: pre, post: post}
|
||||
a.apply(parent, "Node", nil, root)
|
||||
return
|
||||
}
|
||||
|
||||
var abort = new(int) // singleton, to signal termination of Apply
|
||||
|
||||
// A Cursor describes a node encountered during Apply.
|
||||
// Information about the node and its parent is available
|
||||
// from the Node, Parent, Name, and Index methods.
|
||||
//
|
||||
// If p is a variable of type and value of the current parent node
|
||||
// c.Parent(), and f is the field identifier with name c.Name(),
|
||||
// the following invariants hold:
|
||||
//
|
||||
// p.f == c.Node() if c.Index() < 0
|
||||
// p.f[c.Index()] == c.Node() if c.Index() >= 0
|
||||
//
|
||||
// The methods Replace, Delete, InsertBefore, and InsertAfter
|
||||
// can be used to change the AST without disrupting Apply.
|
||||
//
|
||||
// This type is not to be confused with [inspector.Cursor] from
|
||||
// package [golang.org/x/tools/go/ast/inspector], which provides
|
||||
// stateless navigation of immutable syntax trees.
|
||||
type Cursor struct {
|
||||
parent ast.Node
|
||||
name string
|
||||
iter *iterator // valid if non-nil
|
||||
node ast.Node
|
||||
}
|
||||
|
||||
// Node returns the current Node.
|
||||
func (c *Cursor) Node() ast.Node { return c.node }
|
||||
|
||||
// Parent returns the parent of the current Node.
|
||||
func (c *Cursor) Parent() ast.Node { return c.parent }
|
||||
|
||||
// Name returns the name of the parent Node field that contains the current Node.
|
||||
// If the parent is a *ast.Package and the current Node is a *ast.File, Name returns
|
||||
// the filename for the current Node.
|
||||
func (c *Cursor) Name() string { return c.name }
|
||||
|
||||
// Index reports the index >= 0 of the current Node in the slice of Nodes that
|
||||
// contains it, or a value < 0 if the current Node is not part of a slice.
|
||||
// The index of the current node changes if InsertBefore is called while
|
||||
// processing the current node.
|
||||
func (c *Cursor) Index() int {
|
||||
if c.iter != nil {
|
||||
return c.iter.index
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// field returns the current node's parent field value.
|
||||
func (c *Cursor) field() reflect.Value {
|
||||
return reflect.Indirect(reflect.ValueOf(c.parent)).FieldByName(c.name)
|
||||
}
|
||||
|
||||
// Replace replaces the current Node with n.
|
||||
// The replacement node is not walked by Apply.
|
||||
func (c *Cursor) Replace(n ast.Node) {
|
||||
if _, ok := c.node.(*ast.File); ok {
|
||||
file, ok := n.(*ast.File)
|
||||
if !ok {
|
||||
panic("attempt to replace *ast.File with non-*ast.File")
|
||||
}
|
||||
c.parent.(*ast.Package).Files[c.name] = file
|
||||
return
|
||||
}
|
||||
|
||||
v := c.field()
|
||||
if i := c.Index(); i >= 0 {
|
||||
v = v.Index(i)
|
||||
}
|
||||
v.Set(reflect.ValueOf(n))
|
||||
}
|
||||
|
||||
// Delete deletes the current Node from its containing slice.
|
||||
// If the current Node is not part of a slice, Delete panics.
|
||||
// As a special case, if the current node is a package file,
|
||||
// Delete removes it from the package's Files map.
|
||||
func (c *Cursor) Delete() {
|
||||
if _, ok := c.node.(*ast.File); ok {
|
||||
delete(c.parent.(*ast.Package).Files, c.name)
|
||||
return
|
||||
}
|
||||
|
||||
i := c.Index()
|
||||
if i < 0 {
|
||||
panic("Delete node not contained in slice")
|
||||
}
|
||||
v := c.field()
|
||||
l := v.Len()
|
||||
reflect.Copy(v.Slice(i, l), v.Slice(i+1, l))
|
||||
v.Index(l - 1).Set(reflect.Zero(v.Type().Elem()))
|
||||
v.SetLen(l - 1)
|
||||
c.iter.step--
|
||||
}
|
||||
|
||||
// InsertAfter inserts n after the current Node in its containing slice.
|
||||
// If the current Node is not part of a slice, InsertAfter panics.
|
||||
// Apply does not walk n.
|
||||
func (c *Cursor) InsertAfter(n ast.Node) {
|
||||
i := c.Index()
|
||||
if i < 0 {
|
||||
panic("InsertAfter node not contained in slice")
|
||||
}
|
||||
v := c.field()
|
||||
v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem())))
|
||||
l := v.Len()
|
||||
reflect.Copy(v.Slice(i+2, l), v.Slice(i+1, l))
|
||||
v.Index(i + 1).Set(reflect.ValueOf(n))
|
||||
c.iter.step++
|
||||
}
|
||||
|
||||
// InsertBefore inserts n before the current Node in its containing slice.
|
||||
// If the current Node is not part of a slice, InsertBefore panics.
|
||||
// Apply will not walk n.
|
||||
func (c *Cursor) InsertBefore(n ast.Node) {
|
||||
i := c.Index()
|
||||
if i < 0 {
|
||||
panic("InsertBefore node not contained in slice")
|
||||
}
|
||||
v := c.field()
|
||||
v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem())))
|
||||
l := v.Len()
|
||||
reflect.Copy(v.Slice(i+1, l), v.Slice(i, l))
|
||||
v.Index(i).Set(reflect.ValueOf(n))
|
||||
c.iter.index++
|
||||
}
|
||||
|
||||
// application carries all the shared data so we can pass it around cheaply.
|
||||
type application struct {
|
||||
pre, post ApplyFunc
|
||||
cursor Cursor
|
||||
iter iterator
|
||||
}
|
||||
|
||||
func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.Node) {
|
||||
// convert typed nil into untyped nil
|
||||
if v := reflect.ValueOf(n); v.Kind() == reflect.Pointer && v.IsNil() {
|
||||
n = nil
|
||||
}
|
||||
|
||||
// avoid heap-allocating a new cursor for each apply call; reuse a.cursor instead
|
||||
saved := a.cursor
|
||||
a.cursor.parent = parent
|
||||
a.cursor.name = name
|
||||
a.cursor.iter = iter
|
||||
a.cursor.node = n
|
||||
|
||||
if a.pre != nil && !a.pre(&a.cursor) {
|
||||
a.cursor = saved
|
||||
return
|
||||
}
|
||||
|
||||
// walk children
|
||||
// (the order of the cases matches the order of the corresponding node types in go/ast)
|
||||
switch n := n.(type) {
|
||||
case nil:
|
||||
// nothing to do
|
||||
|
||||
// Comments and fields
|
||||
case *ast.Comment:
|
||||
// nothing to do
|
||||
|
||||
case *ast.CommentGroup:
|
||||
if n != nil {
|
||||
a.applyList(n, "List")
|
||||
}
|
||||
|
||||
case *ast.Field:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.applyList(n, "Names")
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.apply(n, "Tag", nil, n.Tag)
|
||||
a.apply(n, "Comment", nil, n.Comment)
|
||||
|
||||
case *ast.FieldList:
|
||||
a.applyList(n, "List")
|
||||
|
||||
// Expressions
|
||||
case *ast.BadExpr, *ast.Ident, *ast.BasicLit:
|
||||
// nothing to do
|
||||
|
||||
case *ast.Ellipsis:
|
||||
a.apply(n, "Elt", nil, n.Elt)
|
||||
|
||||
case *ast.FuncLit:
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.CompositeLit:
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.applyList(n, "Elts")
|
||||
|
||||
case *ast.ParenExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Sel", nil, n.Sel)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Index", nil, n.Index)
|
||||
|
||||
case *ast.IndexListExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.applyList(n, "Indices")
|
||||
|
||||
case *ast.SliceExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Low", nil, n.Low)
|
||||
a.apply(n, "High", nil, n.High)
|
||||
a.apply(n, "Max", nil, n.Max)
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
|
||||
case *ast.CallExpr:
|
||||
a.apply(n, "Fun", nil, n.Fun)
|
||||
a.applyList(n, "Args")
|
||||
|
||||
case *ast.StarExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Y", nil, n.Y)
|
||||
|
||||
case *ast.KeyValueExpr:
|
||||
a.apply(n, "Key", nil, n.Key)
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
|
||||
// Types
|
||||
case *ast.ArrayType:
|
||||
a.apply(n, "Len", nil, n.Len)
|
||||
a.apply(n, "Elt", nil, n.Elt)
|
||||
|
||||
case *ast.StructType:
|
||||
a.apply(n, "Fields", nil, n.Fields)
|
||||
|
||||
case *ast.FuncType:
|
||||
if tparams := n.TypeParams; tparams != nil {
|
||||
a.apply(n, "TypeParams", nil, tparams)
|
||||
}
|
||||
a.apply(n, "Params", nil, n.Params)
|
||||
a.apply(n, "Results", nil, n.Results)
|
||||
|
||||
case *ast.InterfaceType:
|
||||
a.apply(n, "Methods", nil, n.Methods)
|
||||
|
||||
case *ast.MapType:
|
||||
a.apply(n, "Key", nil, n.Key)
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
|
||||
case *ast.ChanType:
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
|
||||
// Statements
|
||||
case *ast.BadStmt:
|
||||
// nothing to do
|
||||
|
||||
case *ast.DeclStmt:
|
||||
a.apply(n, "Decl", nil, n.Decl)
|
||||
|
||||
case *ast.EmptyStmt:
|
||||
// nothing to do
|
||||
|
||||
case *ast.LabeledStmt:
|
||||
a.apply(n, "Label", nil, n.Label)
|
||||
a.apply(n, "Stmt", nil, n.Stmt)
|
||||
|
||||
case *ast.ExprStmt:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.SendStmt:
|
||||
a.apply(n, "Chan", nil, n.Chan)
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
|
||||
case *ast.IncDecStmt:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.AssignStmt:
|
||||
a.applyList(n, "Lhs")
|
||||
a.applyList(n, "Rhs")
|
||||
|
||||
case *ast.GoStmt:
|
||||
a.apply(n, "Call", nil, n.Call)
|
||||
|
||||
case *ast.DeferStmt:
|
||||
a.apply(n, "Call", nil, n.Call)
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
a.applyList(n, "Results")
|
||||
|
||||
case *ast.BranchStmt:
|
||||
a.apply(n, "Label", nil, n.Label)
|
||||
|
||||
case *ast.BlockStmt:
|
||||
a.applyList(n, "List")
|
||||
|
||||
case *ast.IfStmt:
|
||||
a.apply(n, "Init", nil, n.Init)
|
||||
a.apply(n, "Cond", nil, n.Cond)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
a.apply(n, "Else", nil, n.Else)
|
||||
|
||||
case *ast.CaseClause:
|
||||
a.applyList(n, "List")
|
||||
a.applyList(n, "Body")
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
a.apply(n, "Init", nil, n.Init)
|
||||
a.apply(n, "Tag", nil, n.Tag)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
a.apply(n, "Init", nil, n.Init)
|
||||
a.apply(n, "Assign", nil, n.Assign)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.CommClause:
|
||||
a.apply(n, "Comm", nil, n.Comm)
|
||||
a.applyList(n, "Body")
|
||||
|
||||
case *ast.SelectStmt:
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.ForStmt:
|
||||
a.apply(n, "Init", nil, n.Init)
|
||||
a.apply(n, "Cond", nil, n.Cond)
|
||||
a.apply(n, "Post", nil, n.Post)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.RangeStmt:
|
||||
a.apply(n, "Key", nil, n.Key)
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
// Declarations
|
||||
case *ast.ImportSpec:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.apply(n, "Name", nil, n.Name)
|
||||
a.apply(n, "Path", nil, n.Path)
|
||||
a.apply(n, "Comment", nil, n.Comment)
|
||||
|
||||
case *ast.ValueSpec:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.applyList(n, "Names")
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.applyList(n, "Values")
|
||||
a.apply(n, "Comment", nil, n.Comment)
|
||||
|
||||
case *ast.TypeSpec:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.apply(n, "Name", nil, n.Name)
|
||||
if tparams := n.TypeParams; tparams != nil {
|
||||
a.apply(n, "TypeParams", nil, tparams)
|
||||
}
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.apply(n, "Comment", nil, n.Comment)
|
||||
|
||||
case *ast.BadDecl:
|
||||
// nothing to do
|
||||
|
||||
case *ast.GenDecl:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.applyList(n, "Specs")
|
||||
|
||||
case *ast.FuncDecl:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.apply(n, "Recv", nil, n.Recv)
|
||||
a.apply(n, "Name", nil, n.Name)
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
// Files and packages
|
||||
case *ast.File:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.apply(n, "Name", nil, n.Name)
|
||||
a.applyList(n, "Decls")
|
||||
// Don't walk n.Comments; they have either been walked already if
|
||||
// they are Doc comments, or they can be easily walked explicitly.
|
||||
|
||||
case *ast.Package:
|
||||
// collect and sort names for reproducible behavior
|
||||
var names []string
|
||||
for name := range n.Files {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
a.apply(n, name, nil, n.Files[name])
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Apply: unexpected node type %T", n))
|
||||
}
|
||||
|
||||
if a.post != nil && !a.post(&a.cursor) {
|
||||
panic(abort)
|
||||
}
|
||||
|
||||
a.cursor = saved
|
||||
}
|
||||
|
||||
// An iterator controls iteration over a slice of nodes.
|
||||
type iterator struct {
|
||||
index, step int
|
||||
}
|
||||
|
||||
func (a *application) applyList(parent ast.Node, name string) {
|
||||
// avoid heap-allocating a new iterator for each applyList call; reuse a.iter instead
|
||||
saved := a.iter
|
||||
a.iter.index = 0
|
||||
for {
|
||||
// must reload parent.name each time, since cursor modifications might change it
|
||||
v := reflect.Indirect(reflect.ValueOf(parent)).FieldByName(name)
|
||||
if a.iter.index >= v.Len() {
|
||||
break
|
||||
}
|
||||
|
||||
// element x may be nil in a bad AST - be cautious
|
||||
var x ast.Node
|
||||
if e := v.Index(a.iter.index); e.IsValid() {
|
||||
x = e.Interface().(ast.Node)
|
||||
}
|
||||
|
||||
a.iter.step = 1
|
||||
a.apply(parent, name, &a.iter, x)
|
||||
a.iter.index += a.iter.step
|
||||
}
|
||||
a.iter = saved
|
||||
}
|
||||
13
vendor/golang.org/x/tools/go/ast/astutil/util.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/go/ast/astutil/util.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2015 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 astutil
|
||||
|
||||
import "go/ast"
|
||||
|
||||
// Unparen returns e with any enclosing parentheses stripped.
|
||||
// Deprecated: use [ast.Unparen].
|
||||
//
|
||||
//go:fix inline
|
||||
func Unparen(e ast.Expr) ast.Expr { return ast.Unparen(e) }
|
||||
295
vendor/golang.org/x/tools/go/ast/edge/edge.go
generated
vendored
Normal file
295
vendor/golang.org/x/tools/go/ast/edge/edge.go
generated
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
// 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 edge defines identifiers for each field of an ast.Node
|
||||
// struct type that refers to another Node.
|
||||
package edge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A Kind describes a field of an ast.Node struct.
|
||||
type Kind uint8
|
||||
|
||||
// String returns a description of the edge kind.
|
||||
func (k Kind) String() string {
|
||||
if k == Invalid {
|
||||
return "<invalid>"
|
||||
}
|
||||
info := fieldInfos[k]
|
||||
return fmt.Sprintf("%v.%s", info.nodeType.Elem().Name(), info.name)
|
||||
}
|
||||
|
||||
// NodeType returns the pointer-to-struct type of the ast.Node implementation.
|
||||
func (k Kind) NodeType() reflect.Type { return fieldInfos[k].nodeType }
|
||||
|
||||
// FieldName returns the name of the field.
|
||||
func (k Kind) FieldName() string { return fieldInfos[k].name }
|
||||
|
||||
// FieldType returns the declared type of the field.
|
||||
func (k Kind) FieldType() reflect.Type { return fieldInfos[k].fieldType }
|
||||
|
||||
// Get returns the direct child of n identified by (k, idx).
|
||||
// n's type must match k.NodeType().
|
||||
// idx must be a valid slice index, or -1 for a non-slice.
|
||||
func (k Kind) Get(n ast.Node, idx int) ast.Node {
|
||||
if k.NodeType() != reflect.TypeOf(n) {
|
||||
panic(fmt.Sprintf("%v.Get(%T): invalid node type", k, n))
|
||||
}
|
||||
v := reflect.ValueOf(n).Elem().Field(fieldInfos[k].index)
|
||||
if idx != -1 {
|
||||
v = v.Index(idx) // asserts valid index
|
||||
} else {
|
||||
// (The type assertion below asserts that v is not a slice.)
|
||||
}
|
||||
return v.Interface().(ast.Node) // may be nil
|
||||
}
|
||||
|
||||
const (
|
||||
Invalid Kind = iota // for nodes at the root of the traversal
|
||||
|
||||
// Kinds are sorted alphabetically.
|
||||
// Numbering is not stable.
|
||||
// Each is named Type_Field, where Type is the
|
||||
// ast.Node struct type and Field is the name of the field
|
||||
|
||||
ArrayType_Elt
|
||||
ArrayType_Len
|
||||
AssignStmt_Lhs
|
||||
AssignStmt_Rhs
|
||||
BinaryExpr_X
|
||||
BinaryExpr_Y
|
||||
BlockStmt_List
|
||||
BranchStmt_Label
|
||||
CallExpr_Args
|
||||
CallExpr_Fun
|
||||
CaseClause_Body
|
||||
CaseClause_List
|
||||
ChanType_Value
|
||||
CommClause_Body
|
||||
CommClause_Comm
|
||||
CommentGroup_List
|
||||
CompositeLit_Elts
|
||||
CompositeLit_Type
|
||||
DeclStmt_Decl
|
||||
DeferStmt_Call
|
||||
Ellipsis_Elt
|
||||
ExprStmt_X
|
||||
FieldList_List
|
||||
Field_Comment
|
||||
Field_Doc
|
||||
Field_Names
|
||||
Field_Tag
|
||||
Field_Type
|
||||
File_Decls
|
||||
File_Doc
|
||||
File_Name
|
||||
ForStmt_Body
|
||||
ForStmt_Cond
|
||||
ForStmt_Init
|
||||
ForStmt_Post
|
||||
FuncDecl_Body
|
||||
FuncDecl_Doc
|
||||
FuncDecl_Name
|
||||
FuncDecl_Recv
|
||||
FuncDecl_Type
|
||||
FuncLit_Body
|
||||
FuncLit_Type
|
||||
FuncType_Params
|
||||
FuncType_Results
|
||||
FuncType_TypeParams
|
||||
GenDecl_Doc
|
||||
GenDecl_Specs
|
||||
GoStmt_Call
|
||||
IfStmt_Body
|
||||
IfStmt_Cond
|
||||
IfStmt_Else
|
||||
IfStmt_Init
|
||||
ImportSpec_Comment
|
||||
ImportSpec_Doc
|
||||
ImportSpec_Name
|
||||
ImportSpec_Path
|
||||
IncDecStmt_X
|
||||
IndexExpr_Index
|
||||
IndexExpr_X
|
||||
IndexListExpr_Indices
|
||||
IndexListExpr_X
|
||||
InterfaceType_Methods
|
||||
KeyValueExpr_Key
|
||||
KeyValueExpr_Value
|
||||
LabeledStmt_Label
|
||||
LabeledStmt_Stmt
|
||||
MapType_Key
|
||||
MapType_Value
|
||||
ParenExpr_X
|
||||
RangeStmt_Body
|
||||
RangeStmt_Key
|
||||
RangeStmt_Value
|
||||
RangeStmt_X
|
||||
ReturnStmt_Results
|
||||
SelectStmt_Body
|
||||
SelectorExpr_Sel
|
||||
SelectorExpr_X
|
||||
SendStmt_Chan
|
||||
SendStmt_Value
|
||||
SliceExpr_High
|
||||
SliceExpr_Low
|
||||
SliceExpr_Max
|
||||
SliceExpr_X
|
||||
StarExpr_X
|
||||
StructType_Fields
|
||||
SwitchStmt_Body
|
||||
SwitchStmt_Init
|
||||
SwitchStmt_Tag
|
||||
TypeAssertExpr_Type
|
||||
TypeAssertExpr_X
|
||||
TypeSpec_Comment
|
||||
TypeSpec_Doc
|
||||
TypeSpec_Name
|
||||
TypeSpec_Type
|
||||
TypeSpec_TypeParams
|
||||
TypeSwitchStmt_Assign
|
||||
TypeSwitchStmt_Body
|
||||
TypeSwitchStmt_Init
|
||||
UnaryExpr_X
|
||||
ValueSpec_Comment
|
||||
ValueSpec_Doc
|
||||
ValueSpec_Names
|
||||
ValueSpec_Type
|
||||
ValueSpec_Values
|
||||
|
||||
maxKind
|
||||
)
|
||||
|
||||
// Assert that the encoding fits in 7 bits,
|
||||
// as the inspector relies on this.
|
||||
// (We are currently at 104.)
|
||||
var _ = [1 << 7]struct{}{}[maxKind]
|
||||
|
||||
type fieldInfo struct {
|
||||
nodeType reflect.Type // pointer-to-struct type of ast.Node implementation
|
||||
name string
|
||||
index int
|
||||
fieldType reflect.Type
|
||||
}
|
||||
|
||||
func info[N ast.Node](fieldName string) fieldInfo {
|
||||
nodePtrType := reflect.TypeFor[N]()
|
||||
f, ok := nodePtrType.Elem().FieldByName(fieldName)
|
||||
if !ok {
|
||||
panic(fieldName)
|
||||
}
|
||||
return fieldInfo{nodePtrType, fieldName, f.Index[0], f.Type}
|
||||
}
|
||||
|
||||
var fieldInfos = [...]fieldInfo{
|
||||
Invalid: {},
|
||||
ArrayType_Elt: info[*ast.ArrayType]("Elt"),
|
||||
ArrayType_Len: info[*ast.ArrayType]("Len"),
|
||||
AssignStmt_Lhs: info[*ast.AssignStmt]("Lhs"),
|
||||
AssignStmt_Rhs: info[*ast.AssignStmt]("Rhs"),
|
||||
BinaryExpr_X: info[*ast.BinaryExpr]("X"),
|
||||
BinaryExpr_Y: info[*ast.BinaryExpr]("Y"),
|
||||
BlockStmt_List: info[*ast.BlockStmt]("List"),
|
||||
BranchStmt_Label: info[*ast.BranchStmt]("Label"),
|
||||
CallExpr_Args: info[*ast.CallExpr]("Args"),
|
||||
CallExpr_Fun: info[*ast.CallExpr]("Fun"),
|
||||
CaseClause_Body: info[*ast.CaseClause]("Body"),
|
||||
CaseClause_List: info[*ast.CaseClause]("List"),
|
||||
ChanType_Value: info[*ast.ChanType]("Value"),
|
||||
CommClause_Body: info[*ast.CommClause]("Body"),
|
||||
CommClause_Comm: info[*ast.CommClause]("Comm"),
|
||||
CommentGroup_List: info[*ast.CommentGroup]("List"),
|
||||
CompositeLit_Elts: info[*ast.CompositeLit]("Elts"),
|
||||
CompositeLit_Type: info[*ast.CompositeLit]("Type"),
|
||||
DeclStmt_Decl: info[*ast.DeclStmt]("Decl"),
|
||||
DeferStmt_Call: info[*ast.DeferStmt]("Call"),
|
||||
Ellipsis_Elt: info[*ast.Ellipsis]("Elt"),
|
||||
ExprStmt_X: info[*ast.ExprStmt]("X"),
|
||||
FieldList_List: info[*ast.FieldList]("List"),
|
||||
Field_Comment: info[*ast.Field]("Comment"),
|
||||
Field_Doc: info[*ast.Field]("Doc"),
|
||||
Field_Names: info[*ast.Field]("Names"),
|
||||
Field_Tag: info[*ast.Field]("Tag"),
|
||||
Field_Type: info[*ast.Field]("Type"),
|
||||
File_Decls: info[*ast.File]("Decls"),
|
||||
File_Doc: info[*ast.File]("Doc"),
|
||||
File_Name: info[*ast.File]("Name"),
|
||||
ForStmt_Body: info[*ast.ForStmt]("Body"),
|
||||
ForStmt_Cond: info[*ast.ForStmt]("Cond"),
|
||||
ForStmt_Init: info[*ast.ForStmt]("Init"),
|
||||
ForStmt_Post: info[*ast.ForStmt]("Post"),
|
||||
FuncDecl_Body: info[*ast.FuncDecl]("Body"),
|
||||
FuncDecl_Doc: info[*ast.FuncDecl]("Doc"),
|
||||
FuncDecl_Name: info[*ast.FuncDecl]("Name"),
|
||||
FuncDecl_Recv: info[*ast.FuncDecl]("Recv"),
|
||||
FuncDecl_Type: info[*ast.FuncDecl]("Type"),
|
||||
FuncLit_Body: info[*ast.FuncLit]("Body"),
|
||||
FuncLit_Type: info[*ast.FuncLit]("Type"),
|
||||
FuncType_Params: info[*ast.FuncType]("Params"),
|
||||
FuncType_Results: info[*ast.FuncType]("Results"),
|
||||
FuncType_TypeParams: info[*ast.FuncType]("TypeParams"),
|
||||
GenDecl_Doc: info[*ast.GenDecl]("Doc"),
|
||||
GenDecl_Specs: info[*ast.GenDecl]("Specs"),
|
||||
GoStmt_Call: info[*ast.GoStmt]("Call"),
|
||||
IfStmt_Body: info[*ast.IfStmt]("Body"),
|
||||
IfStmt_Cond: info[*ast.IfStmt]("Cond"),
|
||||
IfStmt_Else: info[*ast.IfStmt]("Else"),
|
||||
IfStmt_Init: info[*ast.IfStmt]("Init"),
|
||||
ImportSpec_Comment: info[*ast.ImportSpec]("Comment"),
|
||||
ImportSpec_Doc: info[*ast.ImportSpec]("Doc"),
|
||||
ImportSpec_Name: info[*ast.ImportSpec]("Name"),
|
||||
ImportSpec_Path: info[*ast.ImportSpec]("Path"),
|
||||
IncDecStmt_X: info[*ast.IncDecStmt]("X"),
|
||||
IndexExpr_Index: info[*ast.IndexExpr]("Index"),
|
||||
IndexExpr_X: info[*ast.IndexExpr]("X"),
|
||||
IndexListExpr_Indices: info[*ast.IndexListExpr]("Indices"),
|
||||
IndexListExpr_X: info[*ast.IndexListExpr]("X"),
|
||||
InterfaceType_Methods: info[*ast.InterfaceType]("Methods"),
|
||||
KeyValueExpr_Key: info[*ast.KeyValueExpr]("Key"),
|
||||
KeyValueExpr_Value: info[*ast.KeyValueExpr]("Value"),
|
||||
LabeledStmt_Label: info[*ast.LabeledStmt]("Label"),
|
||||
LabeledStmt_Stmt: info[*ast.LabeledStmt]("Stmt"),
|
||||
MapType_Key: info[*ast.MapType]("Key"),
|
||||
MapType_Value: info[*ast.MapType]("Value"),
|
||||
ParenExpr_X: info[*ast.ParenExpr]("X"),
|
||||
RangeStmt_Body: info[*ast.RangeStmt]("Body"),
|
||||
RangeStmt_Key: info[*ast.RangeStmt]("Key"),
|
||||
RangeStmt_Value: info[*ast.RangeStmt]("Value"),
|
||||
RangeStmt_X: info[*ast.RangeStmt]("X"),
|
||||
ReturnStmt_Results: info[*ast.ReturnStmt]("Results"),
|
||||
SelectStmt_Body: info[*ast.SelectStmt]("Body"),
|
||||
SelectorExpr_Sel: info[*ast.SelectorExpr]("Sel"),
|
||||
SelectorExpr_X: info[*ast.SelectorExpr]("X"),
|
||||
SendStmt_Chan: info[*ast.SendStmt]("Chan"),
|
||||
SendStmt_Value: info[*ast.SendStmt]("Value"),
|
||||
SliceExpr_High: info[*ast.SliceExpr]("High"),
|
||||
SliceExpr_Low: info[*ast.SliceExpr]("Low"),
|
||||
SliceExpr_Max: info[*ast.SliceExpr]("Max"),
|
||||
SliceExpr_X: info[*ast.SliceExpr]("X"),
|
||||
StarExpr_X: info[*ast.StarExpr]("X"),
|
||||
StructType_Fields: info[*ast.StructType]("Fields"),
|
||||
SwitchStmt_Body: info[*ast.SwitchStmt]("Body"),
|
||||
SwitchStmt_Init: info[*ast.SwitchStmt]("Init"),
|
||||
SwitchStmt_Tag: info[*ast.SwitchStmt]("Tag"),
|
||||
TypeAssertExpr_Type: info[*ast.TypeAssertExpr]("Type"),
|
||||
TypeAssertExpr_X: info[*ast.TypeAssertExpr]("X"),
|
||||
TypeSpec_Comment: info[*ast.TypeSpec]("Comment"),
|
||||
TypeSpec_Doc: info[*ast.TypeSpec]("Doc"),
|
||||
TypeSpec_Name: info[*ast.TypeSpec]("Name"),
|
||||
TypeSpec_Type: info[*ast.TypeSpec]("Type"),
|
||||
TypeSpec_TypeParams: info[*ast.TypeSpec]("TypeParams"),
|
||||
TypeSwitchStmt_Assign: info[*ast.TypeSwitchStmt]("Assign"),
|
||||
TypeSwitchStmt_Body: info[*ast.TypeSwitchStmt]("Body"),
|
||||
TypeSwitchStmt_Init: info[*ast.TypeSwitchStmt]("Init"),
|
||||
UnaryExpr_X: info[*ast.UnaryExpr]("X"),
|
||||
ValueSpec_Comment: info[*ast.ValueSpec]("Comment"),
|
||||
ValueSpec_Doc: info[*ast.ValueSpec]("Doc"),
|
||||
ValueSpec_Names: info[*ast.ValueSpec]("Names"),
|
||||
ValueSpec_Type: info[*ast.ValueSpec]("Type"),
|
||||
ValueSpec_Values: info[*ast.ValueSpec]("Values"),
|
||||
}
|
||||
517
vendor/golang.org/x/tools/go/ast/inspector/cursor.go
generated
vendored
Normal file
517
vendor/golang.org/x/tools/go/ast/inspector/cursor.go
generated
vendored
Normal file
@@ -0,0 +1,517 @@
|
||||
// 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 inspector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"iter"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/ast/edge"
|
||||
)
|
||||
|
||||
// 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.
|
||||
type Cursor struct {
|
||||
in *Inspector
|
||||
index int32 // index of push node; -1 for virtual root node
|
||||
}
|
||||
|
||||
// Root returns a cursor for the virtual root node,
|
||||
// whose children are the files provided to [New].
|
||||
//
|
||||
// Its [Cursor.Node] method return nil.
|
||||
func (in *Inspector) Root() Cursor {
|
||||
return Cursor{in, -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 (in *Inspector) At(index int32) Cursor {
|
||||
if index < 0 {
|
||||
panic("negative index")
|
||||
}
|
||||
if int(index) >= len(in.events) {
|
||||
panic("index out of range for this inspector")
|
||||
}
|
||||
if in.events[index].index < index {
|
||||
panic("invalid index") // (a push, not a pop)
|
||||
}
|
||||
return Cursor{in, index}
|
||||
}
|
||||
|
||||
// Inspector returns the cursor's Inspector.
|
||||
func (c Cursor) Inspector() *Inspector { return c.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 [At].
|
||||
//
|
||||
// Index must not be called on the Root node.
|
||||
func (c Cursor) Index() int32 {
|
||||
if c.index < 0 {
|
||||
panic("Index called on Root node")
|
||||
}
|
||||
return c.index
|
||||
}
|
||||
|
||||
// Node returns the node at the current cursor position,
|
||||
// or nil for the cursor returned by [Inspector.Root].
|
||||
func (c Cursor) Node() ast.Node {
|
||||
if c.index < 0 {
|
||||
return nil
|
||||
}
|
||||
return c.in.events[c.index].node
|
||||
}
|
||||
|
||||
// String returns information about the cursor's node, if any.
|
||||
func (c Cursor) String() string {
|
||||
if c.in == nil {
|
||||
return "(invalid)"
|
||||
}
|
||||
if c.index < 0 {
|
||||
return "(root)"
|
||||
}
|
||||
return reflect.TypeOf(c.Node()).String()
|
||||
}
|
||||
|
||||
// indices return the [start, end) half-open interval of event indices.
|
||||
func (c Cursor) indices() (int32, int32) {
|
||||
if c.index < 0 {
|
||||
return 0, int32(len(c.in.events)) // root: all events
|
||||
} else {
|
||||
return c.index, c.in.events[c.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 (c Cursor) Preorder(types ...ast.Node) iter.Seq[Cursor] {
|
||||
mask := maskOf(types)
|
||||
|
||||
return func(yield func(Cursor) bool) {
|
||||
events := c.in.events
|
||||
|
||||
for i, limit := c.indices(); i < limit; {
|
||||
ev := events[i]
|
||||
if ev.index > i { // push?
|
||||
if ev.typ&mask != 0 && !yield(Cursor{c.in, i}) {
|
||||
break
|
||||
}
|
||||
pop := ev.index
|
||||
if events[pop].typ&mask == 0 {
|
||||
// Subtree does not contain types: skip.
|
||||
i = pop + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (c Cursor) Inspect(types []ast.Node, f func(c Cursor) (descend bool)) {
|
||||
mask := maskOf(types)
|
||||
events := c.in.events
|
||||
for i, limit := c.indices(); i < limit; {
|
||||
ev := events[i]
|
||||
if ev.index > i {
|
||||
// push
|
||||
pop := ev.index
|
||||
if ev.typ&mask != 0 && !f(Cursor{c.in, i}) ||
|
||||
events[pop].typ&mask == 0 {
|
||||
// The user opted not to descend, or the
|
||||
// subtree does not contain types:
|
||||
// skip past the pop.
|
||||
i = pop + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (c Cursor) Enclosing(types ...ast.Node) iter.Seq[Cursor] {
|
||||
if c.index < 0 {
|
||||
panic("Cursor.Enclosing called on Root node")
|
||||
}
|
||||
|
||||
mask := maskOf(types)
|
||||
|
||||
return func(yield func(Cursor) bool) {
|
||||
events := c.in.events
|
||||
for i := c.index; i >= 0; i = events[i].parent {
|
||||
if events[i].typ&mask != 0 && !yield(Cursor{c.in, i}) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parent returns the parent of the current node.
|
||||
//
|
||||
// Parent must not be called on the Root node (whose [Cursor.Node] returns nil).
|
||||
func (c Cursor) Parent() Cursor {
|
||||
if c.index < 0 {
|
||||
panic("Cursor.Parent called on Root node")
|
||||
}
|
||||
|
||||
return Cursor{c.in, c.in.events[c.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 (c Cursor) ParentEdge() (edge.Kind, int) {
|
||||
if c.index < 0 {
|
||||
panic("Cursor.ParentEdge called on Root node")
|
||||
}
|
||||
events := c.in.events
|
||||
pop := events[c.index].index
|
||||
return unpackEdgeKindAndIndex(events[pop].parent)
|
||||
}
|
||||
|
||||
// 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 (c Cursor) ChildAt(k edge.Kind, idx int) Cursor {
|
||||
target := packEdgeKindAndIndex(k, idx)
|
||||
|
||||
// Unfortunately there's no shortcut to looping.
|
||||
events := c.in.events
|
||||
i := c.index + 1
|
||||
for {
|
||||
pop := events[i].index
|
||||
if pop < i {
|
||||
break
|
||||
}
|
||||
if events[pop].parent == target {
|
||||
return Cursor{c.in, i}
|
||||
}
|
||||
i = pop + 1
|
||||
}
|
||||
panic(fmt.Sprintf("ChildAt(%v, %d): no such child of %v", k, idx, c))
|
||||
}
|
||||
|
||||
// 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 (c Cursor) Child(n ast.Node) Cursor {
|
||||
if c.index < 0 {
|
||||
panic("Cursor.Child called on Root node")
|
||||
}
|
||||
|
||||
if false {
|
||||
// reference implementation
|
||||
for child := range c.Children() {
|
||||
if child.Node() == n {
|
||||
return child
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// optimized implementation
|
||||
events := c.in.events
|
||||
for i := c.index + 1; events[i].index > i; i = events[i].index + 1 {
|
||||
if events[i].node == n {
|
||||
return Cursor{c.in, i}
|
||||
}
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("Child(%T): not a child of %v", n, c))
|
||||
}
|
||||
|
||||
// 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 (c Cursor) NextSibling() (Cursor, bool) {
|
||||
if c.index < 0 {
|
||||
panic("Cursor.NextSibling called on Root node")
|
||||
}
|
||||
|
||||
events := c.in.events
|
||||
i := events[c.index].index + 1 // after corresponding pop
|
||||
if i < int32(len(events)) {
|
||||
if events[i].index > i { // push?
|
||||
return Cursor{c.in, i}, 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 (c Cursor) PrevSibling() (Cursor, bool) {
|
||||
if c.index < 0 {
|
||||
panic("Cursor.PrevSibling called on Root node")
|
||||
}
|
||||
|
||||
events := c.in.events
|
||||
i := c.index - 1
|
||||
if i >= 0 {
|
||||
if j := events[i].index; j < i { // pop?
|
||||
return Cursor{c.in, j}, true
|
||||
}
|
||||
}
|
||||
return Cursor{}, false
|
||||
}
|
||||
|
||||
// FirstChild returns the first direct child of the current node,
|
||||
// or zero if it has no children.
|
||||
func (c Cursor) FirstChild() (Cursor, bool) {
|
||||
events := c.in.events
|
||||
i := c.index + 1 // i=0 if c is root
|
||||
if i < int32(len(events)) && events[i].index > i { // push?
|
||||
return Cursor{c.in, i}, true
|
||||
}
|
||||
return Cursor{}, false
|
||||
}
|
||||
|
||||
// LastChild returns the last direct child of the current node,
|
||||
// or zero if it has no children.
|
||||
func (c Cursor) LastChild() (Cursor, bool) {
|
||||
events := c.in.events
|
||||
if c.index < 0 { // root?
|
||||
if len(events) > 0 {
|
||||
// return push of final event (a pop)
|
||||
return Cursor{c.in, events[len(events)-1].index}, true
|
||||
}
|
||||
} else {
|
||||
j := events[c.index].index - 1 // before corresponding pop
|
||||
// Inv: j == c.index if c has no children
|
||||
// or j is last child's pop.
|
||||
if j > c.index { // c has children
|
||||
return Cursor{c.in, events[j].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 (c Cursor) Children() iter.Seq[Cursor] {
|
||||
return func(yield func(Cursor) bool) {
|
||||
c, ok := c.FirstChild()
|
||||
for ok && yield(c) {
|
||||
c, ok = c.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 (c Cursor) Contains(c2 Cursor) bool {
|
||||
if c.in != c2.in {
|
||||
panic("different inspectors")
|
||||
}
|
||||
events := c.in.events
|
||||
return c.index <= c2.index && events[c2.index].index <= events[c.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 (c Cursor) FindNode(n ast.Node) (Cursor, bool) {
|
||||
|
||||
// FindNode is equivalent to this code,
|
||||
// but more convenient and 15-20% faster:
|
||||
if false {
|
||||
for candidate := range c.Preorder(n) {
|
||||
if candidate.Node() == n {
|
||||
return candidate, true
|
||||
}
|
||||
}
|
||||
return Cursor{}, false
|
||||
}
|
||||
|
||||
// TODO(adonovan): opt: should we assume Node.Pos is accurate
|
||||
// and combine type-based filtering with position filtering
|
||||
// like FindByPos?
|
||||
|
||||
mask := maskOf([]ast.Node{n})
|
||||
events := c.in.events
|
||||
|
||||
for i, limit := c.indices(); i < limit; i++ {
|
||||
ev := events[i]
|
||||
if ev.index > i { // push?
|
||||
if ev.typ&mask != 0 && ev.node == n {
|
||||
return Cursor{c.in, i}, true
|
||||
}
|
||||
pop := ev.index
|
||||
if events[pop].typ&mask == 0 {
|
||||
// Subtree does not contain type of n: skip.
|
||||
i = pop
|
||||
}
|
||||
}
|
||||
}
|
||||
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.)
|
||||
//
|
||||
// It returns zero if none is found.
|
||||
// Precondition: start <= end.
|
||||
//
|
||||
// See also [astutil.PathEnclosingInterval], which
|
||||
// tolerates adjoining whitespace.
|
||||
func (c Cursor) FindByPos(start, end token.Pos) (Cursor, bool) {
|
||||
if end < start {
|
||||
panic("end < start")
|
||||
}
|
||||
events := c.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.)
|
||||
best := int32(-1)
|
||||
for i, limit := c.indices(); i < limit; i++ {
|
||||
ev := events[i]
|
||||
if ev.index > i { // push?
|
||||
n := ev.node
|
||||
var nodeEnd token.Pos
|
||||
if file, ok := n.(*ast.File); ok {
|
||||
nodeEnd = file.FileEnd
|
||||
// Note: files may be out of Pos order.
|
||||
if file.FileStart > start {
|
||||
i = ev.index // disjoint, after; skip to next file
|
||||
continue
|
||||
}
|
||||
} 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 FuncDecl
|
||||
if ev.typ == 1<<nFuncType &&
|
||||
events[ev.parent].typ == 1<<nFuncDecl &&
|
||||
best > ev.parent {
|
||||
continue
|
||||
}
|
||||
|
||||
nodeEnd = n.End()
|
||||
if n.Pos() > start {
|
||||
break // disjoint, after; stop
|
||||
}
|
||||
}
|
||||
// Inv: node.{Pos,FileStart} <= start
|
||||
if end <= nodeEnd {
|
||||
// node fully contains target range
|
||||
best = i
|
||||
} else if nodeEnd < start {
|
||||
i = ev.index // disjoint, before; skip forward
|
||||
}
|
||||
}
|
||||
}
|
||||
if best >= 0 {
|
||||
return Cursor{c.in, best}, true
|
||||
}
|
||||
return Cursor{}, false
|
||||
}
|
||||
311
vendor/golang.org/x/tools/go/ast/inspector/inspector.go
generated
vendored
Normal file
311
vendor/golang.org/x/tools/go/ast/inspector/inspector.go
generated
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
// Copyright 2018 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 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.
|
||||
package inspector
|
||||
|
||||
// There are four orthogonal features in a traversal:
|
||||
// 1 type filtering
|
||||
// 2 pruning
|
||||
// 3 postorder calls to f
|
||||
// 4 stack
|
||||
// Rather than offer all of them in the API,
|
||||
// only a few combinations are exposed:
|
||||
// - Preorder is the fastest and has fewest features,
|
||||
// but is the most commonly needed traversal.
|
||||
// - Nodes and WithStack both provide pruning and postorder calls,
|
||||
// even though few clients need it, because supporting two versions
|
||||
// is not justified.
|
||||
// More combinations could be supported by expressing them as
|
||||
// wrappers around a more generic traversal, but this was measured
|
||||
// and found to degrade performance significantly (30%).
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"golang.org/x/tools/go/ast/edge"
|
||||
)
|
||||
|
||||
// An Inspector provides methods for inspecting
|
||||
// (traversing) the syntax trees of a package.
|
||||
type Inspector struct {
|
||||
events []event
|
||||
}
|
||||
|
||||
func packEdgeKindAndIndex(ek edge.Kind, index int) int32 {
|
||||
return int32(uint32(index+1)<<7 | uint32(ek))
|
||||
}
|
||||
|
||||
// unpackEdgeKindAndIndex unpacks the edge kind and edge index (within
|
||||
// an []ast.Node slice) from the parent field of a pop event.
|
||||
func unpackEdgeKindAndIndex(x int32) (edge.Kind, int) {
|
||||
// The "parent" field of a pop node holds the
|
||||
// edge Kind in the lower 7 bits and the index+1
|
||||
// in the upper 25.
|
||||
return edge.Kind(x & 0x7f), int(x>>7) - 1
|
||||
}
|
||||
|
||||
// New returns an Inspector for the specified syntax trees.
|
||||
func New(files []*ast.File) *Inspector {
|
||||
return &Inspector{traverse(files)}
|
||||
}
|
||||
|
||||
// An event represents a push or a pop
|
||||
// of an ast.Node during a traversal.
|
||||
type event struct {
|
||||
node ast.Node
|
||||
typ uint64 // typeOf(node) on push event, or union of typ strictly between push and pop events on pop events
|
||||
index int32 // index of corresponding push or pop event
|
||||
parent int32 // index of parent's push node (push nodes only), or packed edge kind/index (pop nodes only)
|
||||
}
|
||||
|
||||
// TODO: Experiment with storing only the second word of event.node (unsafe.Pointer).
|
||||
// Type can be recovered from the sole bit in typ.
|
||||
// [Tried this, wasn't faster. --adonovan]
|
||||
|
||||
// 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 (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
|
||||
// Because it avoids postorder calls to f, and the pruning
|
||||
// check, Preorder is almost twice as fast as Nodes. The two
|
||||
// features seem to contribute similar slowdowns (~1.4x each).
|
||||
|
||||
// This function is equivalent to the PreorderSeq call below,
|
||||
// but to avoid the additional dynamic call (which adds 13-35%
|
||||
// to the benchmarks), we expand it out.
|
||||
//
|
||||
// in.PreorderSeq(types...)(func(n ast.Node) bool {
|
||||
// f(n)
|
||||
// return true
|
||||
// })
|
||||
|
||||
mask := maskOf(types)
|
||||
for i := int32(0); i < int32(len(in.events)); {
|
||||
ev := in.events[i]
|
||||
if ev.index > i {
|
||||
// push
|
||||
if ev.typ&mask != 0 {
|
||||
f(ev.node)
|
||||
}
|
||||
pop := ev.index
|
||||
if in.events[pop].typ&mask == 0 {
|
||||
// Subtrees do not contain types: skip them and pop.
|
||||
i = pop + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool)) {
|
||||
mask := maskOf(types)
|
||||
for i := int32(0); i < int32(len(in.events)); {
|
||||
ev := in.events[i]
|
||||
if ev.index > i {
|
||||
// push
|
||||
pop := ev.index
|
||||
if ev.typ&mask != 0 {
|
||||
if !f(ev.node, true) {
|
||||
i = pop + 1 // jump to corresponding pop + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
if in.events[pop].typ&mask == 0 {
|
||||
// Subtrees do not contain types: skip them.
|
||||
i = pop
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// pop
|
||||
push := ev.index
|
||||
if in.events[push].typ&mask != 0 {
|
||||
f(ev.node, false)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// })
|
||||
func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool)) {
|
||||
mask := maskOf(types)
|
||||
var stack []ast.Node
|
||||
for i := int32(0); i < int32(len(in.events)); {
|
||||
ev := in.events[i]
|
||||
if ev.index > i {
|
||||
// push
|
||||
pop := ev.index
|
||||
stack = append(stack, ev.node)
|
||||
if ev.typ&mask != 0 {
|
||||
if !f(ev.node, true, stack) {
|
||||
i = pop + 1
|
||||
stack = stack[:len(stack)-1]
|
||||
continue
|
||||
}
|
||||
}
|
||||
if in.events[pop].typ&mask == 0 {
|
||||
// Subtrees does not contain types: skip them.
|
||||
i = pop
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// pop
|
||||
push := ev.index
|
||||
if in.events[push].typ&mask != 0 {
|
||||
f(ev.node, false, stack)
|
||||
}
|
||||
stack = stack[:len(stack)-1]
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// traverse builds the table of events representing a traversal.
|
||||
func traverse(files []*ast.File) []event {
|
||||
// Preallocate approximate number of events
|
||||
// based on source file extent of the declarations.
|
||||
// (We use End-Pos not FileStart-FileEnd to neglect
|
||||
// the effect of long doc comments.)
|
||||
// This makes traverse faster by 4x (!).
|
||||
var extent int
|
||||
for _, f := range files {
|
||||
extent += int(f.End() - f.Pos())
|
||||
}
|
||||
// This estimate is based on the net/http package.
|
||||
capacity := min(extent*33/100, 1e6) // impose some reasonable maximum (1M)
|
||||
|
||||
v := &visitor{
|
||||
events: make([]event, 0, capacity),
|
||||
stack: []item{{index: -1}}, // include an extra event so file nodes have a parent
|
||||
}
|
||||
for _, file := range files {
|
||||
walk(v, edge.Invalid, -1, file)
|
||||
}
|
||||
return v.events
|
||||
}
|
||||
|
||||
type visitor struct {
|
||||
events []event
|
||||
stack []item
|
||||
}
|
||||
|
||||
type item struct {
|
||||
index int32 // index of current node's push event
|
||||
parentIndex int32 // index of parent node's push event
|
||||
typAccum uint64 // accumulated type bits of current node's descendants
|
||||
edgeKindAndIndex int32 // edge.Kind and index, bit packed
|
||||
}
|
||||
|
||||
func (v *visitor) push(ek edge.Kind, eindex int, node ast.Node) {
|
||||
var (
|
||||
index = int32(len(v.events))
|
||||
parentIndex = v.stack[len(v.stack)-1].index
|
||||
)
|
||||
v.events = append(v.events, event{
|
||||
node: node,
|
||||
parent: parentIndex,
|
||||
typ: typeOf(node),
|
||||
index: 0, // (pop index is set later by visitor.pop)
|
||||
})
|
||||
v.stack = append(v.stack, item{
|
||||
index: index,
|
||||
parentIndex: parentIndex,
|
||||
edgeKindAndIndex: packEdgeKindAndIndex(ek, eindex),
|
||||
})
|
||||
|
||||
// 2B nodes ought to be enough for anyone!
|
||||
if int32(len(v.events)) < 0 {
|
||||
panic("event index exceeded int32")
|
||||
}
|
||||
|
||||
// 32M elements in an []ast.Node ought to be enough for anyone!
|
||||
if ek2, eindex2 := unpackEdgeKindAndIndex(packEdgeKindAndIndex(ek, eindex)); ek2 != ek || eindex2 != eindex {
|
||||
panic("Node slice index exceeded uint25")
|
||||
}
|
||||
}
|
||||
|
||||
func (v *visitor) pop(node ast.Node) {
|
||||
top := len(v.stack) - 1
|
||||
current := v.stack[top]
|
||||
|
||||
push := &v.events[current.index]
|
||||
parent := &v.stack[top-1]
|
||||
|
||||
push.index = int32(len(v.events)) // make push event refer to pop
|
||||
parent.typAccum |= current.typAccum | push.typ // accumulate type bits into parent
|
||||
|
||||
v.stack = v.stack[:top]
|
||||
|
||||
v.events = append(v.events, event{
|
||||
node: node,
|
||||
typ: current.typAccum,
|
||||
index: current.index,
|
||||
parent: current.edgeKindAndIndex, // see [unpackEdgeKindAndIndex]
|
||||
})
|
||||
}
|
||||
85
vendor/golang.org/x/tools/go/ast/inspector/iter.go
generated
vendored
Normal file
85
vendor/golang.org/x/tools/go/ast/inspector/iter.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build go1.23
|
||||
|
||||
package inspector
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"iter"
|
||||
)
|
||||
|
||||
// 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 (in *Inspector) PreorderSeq(types ...ast.Node) iter.Seq[ast.Node] {
|
||||
|
||||
// This implementation is identical to Preorder,
|
||||
// except that it supports breaking out of the loop.
|
||||
|
||||
return func(yield func(ast.Node) bool) {
|
||||
mask := maskOf(types)
|
||||
for i := int32(0); i < int32(len(in.events)); {
|
||||
ev := in.events[i]
|
||||
if ev.index > i {
|
||||
// push
|
||||
if ev.typ&mask != 0 {
|
||||
if !yield(ev.node) {
|
||||
break
|
||||
}
|
||||
}
|
||||
pop := ev.index
|
||||
if in.events[pop].typ&mask == 0 {
|
||||
// Subtrees do not contain types: skip them and pop.
|
||||
i = pop + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) { ... }
|
||||
func All[N interface {
|
||||
*S
|
||||
ast.Node
|
||||
}, S any](in *Inspector) iter.Seq[N] {
|
||||
|
||||
// To avoid additional dynamic call overheads,
|
||||
// we duplicate rather than call the logic of PreorderSeq.
|
||||
|
||||
mask := typeOf((N)(nil))
|
||||
return func(yield func(N) bool) {
|
||||
for i := int32(0); i < int32(len(in.events)); {
|
||||
ev := in.events[i]
|
||||
if ev.index > i {
|
||||
// push
|
||||
if ev.typ&mask != 0 {
|
||||
if !yield(ev.node.(N)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
pop := ev.index
|
||||
if in.events[pop].typ&mask == 0 {
|
||||
// Subtrees do not contain types: skip them and pop.
|
||||
i = pop + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
227
vendor/golang.org/x/tools/go/ast/inspector/typeof.go
generated
vendored
Normal file
227
vendor/golang.org/x/tools/go/ast/inspector/typeof.go
generated
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
// Copyright 2018 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 inspector
|
||||
|
||||
// This file defines func typeOf(ast.Node) uint64.
|
||||
//
|
||||
// The initial map-based implementation was too slow;
|
||||
// see https://go-review.googlesource.com/c/tools/+/135655/1/go/ast/inspector/inspector.go#196
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
nArrayType = iota
|
||||
nAssignStmt
|
||||
nBadDecl
|
||||
nBadExpr
|
||||
nBadStmt
|
||||
nBasicLit
|
||||
nBinaryExpr
|
||||
nBlockStmt
|
||||
nBranchStmt
|
||||
nCallExpr
|
||||
nCaseClause
|
||||
nChanType
|
||||
nCommClause
|
||||
nComment
|
||||
nCommentGroup
|
||||
nCompositeLit
|
||||
nDeclStmt
|
||||
nDeferStmt
|
||||
nEllipsis
|
||||
nEmptyStmt
|
||||
nExprStmt
|
||||
nField
|
||||
nFieldList
|
||||
nFile
|
||||
nForStmt
|
||||
nFuncDecl
|
||||
nFuncLit
|
||||
nFuncType
|
||||
nGenDecl
|
||||
nGoStmt
|
||||
nIdent
|
||||
nIfStmt
|
||||
nImportSpec
|
||||
nIncDecStmt
|
||||
nIndexExpr
|
||||
nIndexListExpr
|
||||
nInterfaceType
|
||||
nKeyValueExpr
|
||||
nLabeledStmt
|
||||
nMapType
|
||||
nPackage
|
||||
nParenExpr
|
||||
nRangeStmt
|
||||
nReturnStmt
|
||||
nSelectStmt
|
||||
nSelectorExpr
|
||||
nSendStmt
|
||||
nSliceExpr
|
||||
nStarExpr
|
||||
nStructType
|
||||
nSwitchStmt
|
||||
nTypeAssertExpr
|
||||
nTypeSpec
|
||||
nTypeSwitchStmt
|
||||
nUnaryExpr
|
||||
nValueSpec
|
||||
)
|
||||
|
||||
// typeOf returns a distinct single-bit value that represents the type of n.
|
||||
//
|
||||
// Various implementations were benchmarked with BenchmarkNewInspector:
|
||||
//
|
||||
// GOGC=off
|
||||
// - type switch 4.9-5.5ms 2.1ms
|
||||
// - binary search over a sorted list of types 5.5-5.9ms 2.5ms
|
||||
// - linear scan, frequency-ordered list 5.9-6.1ms 2.7ms
|
||||
// - linear scan, unordered list 6.4ms 2.7ms
|
||||
// - hash table 6.5ms 3.1ms
|
||||
//
|
||||
// A perfect hash seemed like overkill.
|
||||
//
|
||||
// The compiler's switch statement is the clear winner
|
||||
// as it produces a binary tree in code,
|
||||
// with constant conditions and good branch prediction.
|
||||
// (Sadly it is the most verbose in source code.)
|
||||
// Binary search suffered from poor branch prediction.
|
||||
func typeOf(n ast.Node) uint64 {
|
||||
// Fast path: nearly half of all nodes are identifiers.
|
||||
if _, ok := n.(*ast.Ident); ok {
|
||||
return 1 << nIdent
|
||||
}
|
||||
|
||||
// These cases include all nodes encountered by ast.Inspect.
|
||||
switch n.(type) {
|
||||
case *ast.ArrayType:
|
||||
return 1 << nArrayType
|
||||
case *ast.AssignStmt:
|
||||
return 1 << nAssignStmt
|
||||
case *ast.BadDecl:
|
||||
return 1 << nBadDecl
|
||||
case *ast.BadExpr:
|
||||
return 1 << nBadExpr
|
||||
case *ast.BadStmt:
|
||||
return 1 << nBadStmt
|
||||
case *ast.BasicLit:
|
||||
return 1 << nBasicLit
|
||||
case *ast.BinaryExpr:
|
||||
return 1 << nBinaryExpr
|
||||
case *ast.BlockStmt:
|
||||
return 1 << nBlockStmt
|
||||
case *ast.BranchStmt:
|
||||
return 1 << nBranchStmt
|
||||
case *ast.CallExpr:
|
||||
return 1 << nCallExpr
|
||||
case *ast.CaseClause:
|
||||
return 1 << nCaseClause
|
||||
case *ast.ChanType:
|
||||
return 1 << nChanType
|
||||
case *ast.CommClause:
|
||||
return 1 << nCommClause
|
||||
case *ast.Comment:
|
||||
return 1 << nComment
|
||||
case *ast.CommentGroup:
|
||||
return 1 << nCommentGroup
|
||||
case *ast.CompositeLit:
|
||||
return 1 << nCompositeLit
|
||||
case *ast.DeclStmt:
|
||||
return 1 << nDeclStmt
|
||||
case *ast.DeferStmt:
|
||||
return 1 << nDeferStmt
|
||||
case *ast.Ellipsis:
|
||||
return 1 << nEllipsis
|
||||
case *ast.EmptyStmt:
|
||||
return 1 << nEmptyStmt
|
||||
case *ast.ExprStmt:
|
||||
return 1 << nExprStmt
|
||||
case *ast.Field:
|
||||
return 1 << nField
|
||||
case *ast.FieldList:
|
||||
return 1 << nFieldList
|
||||
case *ast.File:
|
||||
return 1 << nFile
|
||||
case *ast.ForStmt:
|
||||
return 1 << nForStmt
|
||||
case *ast.FuncDecl:
|
||||
return 1 << nFuncDecl
|
||||
case *ast.FuncLit:
|
||||
return 1 << nFuncLit
|
||||
case *ast.FuncType:
|
||||
return 1 << nFuncType
|
||||
case *ast.GenDecl:
|
||||
return 1 << nGenDecl
|
||||
case *ast.GoStmt:
|
||||
return 1 << nGoStmt
|
||||
case *ast.Ident:
|
||||
return 1 << nIdent
|
||||
case *ast.IfStmt:
|
||||
return 1 << nIfStmt
|
||||
case *ast.ImportSpec:
|
||||
return 1 << nImportSpec
|
||||
case *ast.IncDecStmt:
|
||||
return 1 << nIncDecStmt
|
||||
case *ast.IndexExpr:
|
||||
return 1 << nIndexExpr
|
||||
case *ast.IndexListExpr:
|
||||
return 1 << nIndexListExpr
|
||||
case *ast.InterfaceType:
|
||||
return 1 << nInterfaceType
|
||||
case *ast.KeyValueExpr:
|
||||
return 1 << nKeyValueExpr
|
||||
case *ast.LabeledStmt:
|
||||
return 1 << nLabeledStmt
|
||||
case *ast.MapType:
|
||||
return 1 << nMapType
|
||||
case *ast.Package:
|
||||
return 1 << nPackage
|
||||
case *ast.ParenExpr:
|
||||
return 1 << nParenExpr
|
||||
case *ast.RangeStmt:
|
||||
return 1 << nRangeStmt
|
||||
case *ast.ReturnStmt:
|
||||
return 1 << nReturnStmt
|
||||
case *ast.SelectStmt:
|
||||
return 1 << nSelectStmt
|
||||
case *ast.SelectorExpr:
|
||||
return 1 << nSelectorExpr
|
||||
case *ast.SendStmt:
|
||||
return 1 << nSendStmt
|
||||
case *ast.SliceExpr:
|
||||
return 1 << nSliceExpr
|
||||
case *ast.StarExpr:
|
||||
return 1 << nStarExpr
|
||||
case *ast.StructType:
|
||||
return 1 << nStructType
|
||||
case *ast.SwitchStmt:
|
||||
return 1 << nSwitchStmt
|
||||
case *ast.TypeAssertExpr:
|
||||
return 1 << nTypeAssertExpr
|
||||
case *ast.TypeSpec:
|
||||
return 1 << nTypeSpec
|
||||
case *ast.TypeSwitchStmt:
|
||||
return 1 << nTypeSwitchStmt
|
||||
case *ast.UnaryExpr:
|
||||
return 1 << nUnaryExpr
|
||||
case *ast.ValueSpec:
|
||||
return 1 << nValueSpec
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func maskOf(nodes []ast.Node) uint64 {
|
||||
if len(nodes) == 0 {
|
||||
return math.MaxUint64 // match all node types
|
||||
}
|
||||
var mask uint64
|
||||
for _, n := range nodes {
|
||||
mask |= typeOf(n)
|
||||
}
|
||||
return mask
|
||||
}
|
||||
341
vendor/golang.org/x/tools/go/ast/inspector/walk.go
generated
vendored
Normal file
341
vendor/golang.org/x/tools/go/ast/inspector/walk.go
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
// 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 inspector
|
||||
|
||||
// This file is a fork of ast.Inspect to reduce unnecessary dynamic
|
||||
// calls and to gather edge information.
|
||||
//
|
||||
// Consistency with the original is ensured by TestInspectAllNodes.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"golang.org/x/tools/go/ast/edge"
|
||||
)
|
||||
|
||||
func walkList[N ast.Node](v *visitor, ek edge.Kind, list []N) {
|
||||
for i, node := range list {
|
||||
walk(v, ek, i, node)
|
||||
}
|
||||
}
|
||||
|
||||
func walk(v *visitor, ek edge.Kind, index int, node ast.Node) {
|
||||
v.push(ek, index, node)
|
||||
|
||||
// walk children
|
||||
// (the order of the cases matches the order
|
||||
// of the corresponding node types in ast.go)
|
||||
switch n := node.(type) {
|
||||
// Comments and fields
|
||||
case *ast.Comment:
|
||||
// nothing to do
|
||||
|
||||
case *ast.CommentGroup:
|
||||
walkList(v, edge.CommentGroup_List, n.List)
|
||||
|
||||
case *ast.Field:
|
||||
if n.Doc != nil {
|
||||
walk(v, edge.Field_Doc, -1, n.Doc)
|
||||
}
|
||||
walkList(v, edge.Field_Names, n.Names)
|
||||
if n.Type != nil {
|
||||
walk(v, edge.Field_Type, -1, n.Type)
|
||||
}
|
||||
if n.Tag != nil {
|
||||
walk(v, edge.Field_Tag, -1, n.Tag)
|
||||
}
|
||||
if n.Comment != nil {
|
||||
walk(v, edge.Field_Comment, -1, n.Comment)
|
||||
}
|
||||
|
||||
case *ast.FieldList:
|
||||
walkList(v, edge.FieldList_List, n.List)
|
||||
|
||||
// Expressions
|
||||
case *ast.BadExpr, *ast.Ident, *ast.BasicLit:
|
||||
// nothing to do
|
||||
|
||||
case *ast.Ellipsis:
|
||||
if n.Elt != nil {
|
||||
walk(v, edge.Ellipsis_Elt, -1, n.Elt)
|
||||
}
|
||||
|
||||
case *ast.FuncLit:
|
||||
walk(v, edge.FuncLit_Type, -1, n.Type)
|
||||
walk(v, edge.FuncLit_Body, -1, n.Body)
|
||||
|
||||
case *ast.CompositeLit:
|
||||
if n.Type != nil {
|
||||
walk(v, edge.CompositeLit_Type, -1, n.Type)
|
||||
}
|
||||
walkList(v, edge.CompositeLit_Elts, n.Elts)
|
||||
|
||||
case *ast.ParenExpr:
|
||||
walk(v, edge.ParenExpr_X, -1, n.X)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
walk(v, edge.SelectorExpr_X, -1, n.X)
|
||||
walk(v, edge.SelectorExpr_Sel, -1, n.Sel)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
walk(v, edge.IndexExpr_X, -1, n.X)
|
||||
walk(v, edge.IndexExpr_Index, -1, n.Index)
|
||||
|
||||
case *ast.IndexListExpr:
|
||||
walk(v, edge.IndexListExpr_X, -1, n.X)
|
||||
walkList(v, edge.IndexListExpr_Indices, n.Indices)
|
||||
|
||||
case *ast.SliceExpr:
|
||||
walk(v, edge.SliceExpr_X, -1, n.X)
|
||||
if n.Low != nil {
|
||||
walk(v, edge.SliceExpr_Low, -1, n.Low)
|
||||
}
|
||||
if n.High != nil {
|
||||
walk(v, edge.SliceExpr_High, -1, n.High)
|
||||
}
|
||||
if n.Max != nil {
|
||||
walk(v, edge.SliceExpr_Max, -1, n.Max)
|
||||
}
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
walk(v, edge.TypeAssertExpr_X, -1, n.X)
|
||||
if n.Type != nil {
|
||||
walk(v, edge.TypeAssertExpr_Type, -1, n.Type)
|
||||
}
|
||||
|
||||
case *ast.CallExpr:
|
||||
walk(v, edge.CallExpr_Fun, -1, n.Fun)
|
||||
walkList(v, edge.CallExpr_Args, n.Args)
|
||||
|
||||
case *ast.StarExpr:
|
||||
walk(v, edge.StarExpr_X, -1, n.X)
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
walk(v, edge.UnaryExpr_X, -1, n.X)
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
walk(v, edge.BinaryExpr_X, -1, n.X)
|
||||
walk(v, edge.BinaryExpr_Y, -1, n.Y)
|
||||
|
||||
case *ast.KeyValueExpr:
|
||||
walk(v, edge.KeyValueExpr_Key, -1, n.Key)
|
||||
walk(v, edge.KeyValueExpr_Value, -1, n.Value)
|
||||
|
||||
// Types
|
||||
case *ast.ArrayType:
|
||||
if n.Len != nil {
|
||||
walk(v, edge.ArrayType_Len, -1, n.Len)
|
||||
}
|
||||
walk(v, edge.ArrayType_Elt, -1, n.Elt)
|
||||
|
||||
case *ast.StructType:
|
||||
walk(v, edge.StructType_Fields, -1, n.Fields)
|
||||
|
||||
case *ast.FuncType:
|
||||
if n.TypeParams != nil {
|
||||
walk(v, edge.FuncType_TypeParams, -1, n.TypeParams)
|
||||
}
|
||||
if n.Params != nil {
|
||||
walk(v, edge.FuncType_Params, -1, n.Params)
|
||||
}
|
||||
if n.Results != nil {
|
||||
walk(v, edge.FuncType_Results, -1, n.Results)
|
||||
}
|
||||
|
||||
case *ast.InterfaceType:
|
||||
walk(v, edge.InterfaceType_Methods, -1, n.Methods)
|
||||
|
||||
case *ast.MapType:
|
||||
walk(v, edge.MapType_Key, -1, n.Key)
|
||||
walk(v, edge.MapType_Value, -1, n.Value)
|
||||
|
||||
case *ast.ChanType:
|
||||
walk(v, edge.ChanType_Value, -1, n.Value)
|
||||
|
||||
// Statements
|
||||
case *ast.BadStmt:
|
||||
// nothing to do
|
||||
|
||||
case *ast.DeclStmt:
|
||||
walk(v, edge.DeclStmt_Decl, -1, n.Decl)
|
||||
|
||||
case *ast.EmptyStmt:
|
||||
// nothing to do
|
||||
|
||||
case *ast.LabeledStmt:
|
||||
walk(v, edge.LabeledStmt_Label, -1, n.Label)
|
||||
walk(v, edge.LabeledStmt_Stmt, -1, n.Stmt)
|
||||
|
||||
case *ast.ExprStmt:
|
||||
walk(v, edge.ExprStmt_X, -1, n.X)
|
||||
|
||||
case *ast.SendStmt:
|
||||
walk(v, edge.SendStmt_Chan, -1, n.Chan)
|
||||
walk(v, edge.SendStmt_Value, -1, n.Value)
|
||||
|
||||
case *ast.IncDecStmt:
|
||||
walk(v, edge.IncDecStmt_X, -1, n.X)
|
||||
|
||||
case *ast.AssignStmt:
|
||||
walkList(v, edge.AssignStmt_Lhs, n.Lhs)
|
||||
walkList(v, edge.AssignStmt_Rhs, n.Rhs)
|
||||
|
||||
case *ast.GoStmt:
|
||||
walk(v, edge.GoStmt_Call, -1, n.Call)
|
||||
|
||||
case *ast.DeferStmt:
|
||||
walk(v, edge.DeferStmt_Call, -1, n.Call)
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
walkList(v, edge.ReturnStmt_Results, n.Results)
|
||||
|
||||
case *ast.BranchStmt:
|
||||
if n.Label != nil {
|
||||
walk(v, edge.BranchStmt_Label, -1, n.Label)
|
||||
}
|
||||
|
||||
case *ast.BlockStmt:
|
||||
walkList(v, edge.BlockStmt_List, n.List)
|
||||
|
||||
case *ast.IfStmt:
|
||||
if n.Init != nil {
|
||||
walk(v, edge.IfStmt_Init, -1, n.Init)
|
||||
}
|
||||
walk(v, edge.IfStmt_Cond, -1, n.Cond)
|
||||
walk(v, edge.IfStmt_Body, -1, n.Body)
|
||||
if n.Else != nil {
|
||||
walk(v, edge.IfStmt_Else, -1, n.Else)
|
||||
}
|
||||
|
||||
case *ast.CaseClause:
|
||||
walkList(v, edge.CaseClause_List, n.List)
|
||||
walkList(v, edge.CaseClause_Body, n.Body)
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
if n.Init != nil {
|
||||
walk(v, edge.SwitchStmt_Init, -1, n.Init)
|
||||
}
|
||||
if n.Tag != nil {
|
||||
walk(v, edge.SwitchStmt_Tag, -1, n.Tag)
|
||||
}
|
||||
walk(v, edge.SwitchStmt_Body, -1, n.Body)
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
if n.Init != nil {
|
||||
walk(v, edge.TypeSwitchStmt_Init, -1, n.Init)
|
||||
}
|
||||
walk(v, edge.TypeSwitchStmt_Assign, -1, n.Assign)
|
||||
walk(v, edge.TypeSwitchStmt_Body, -1, n.Body)
|
||||
|
||||
case *ast.CommClause:
|
||||
if n.Comm != nil {
|
||||
walk(v, edge.CommClause_Comm, -1, n.Comm)
|
||||
}
|
||||
walkList(v, edge.CommClause_Body, n.Body)
|
||||
|
||||
case *ast.SelectStmt:
|
||||
walk(v, edge.SelectStmt_Body, -1, n.Body)
|
||||
|
||||
case *ast.ForStmt:
|
||||
if n.Init != nil {
|
||||
walk(v, edge.ForStmt_Init, -1, n.Init)
|
||||
}
|
||||
if n.Cond != nil {
|
||||
walk(v, edge.ForStmt_Cond, -1, n.Cond)
|
||||
}
|
||||
if n.Post != nil {
|
||||
walk(v, edge.ForStmt_Post, -1, n.Post)
|
||||
}
|
||||
walk(v, edge.ForStmt_Body, -1, n.Body)
|
||||
|
||||
case *ast.RangeStmt:
|
||||
if n.Key != nil {
|
||||
walk(v, edge.RangeStmt_Key, -1, n.Key)
|
||||
}
|
||||
if n.Value != nil {
|
||||
walk(v, edge.RangeStmt_Value, -1, n.Value)
|
||||
}
|
||||
walk(v, edge.RangeStmt_X, -1, n.X)
|
||||
walk(v, edge.RangeStmt_Body, -1, n.Body)
|
||||
|
||||
// Declarations
|
||||
case *ast.ImportSpec:
|
||||
if n.Doc != nil {
|
||||
walk(v, edge.ImportSpec_Doc, -1, n.Doc)
|
||||
}
|
||||
if n.Name != nil {
|
||||
walk(v, edge.ImportSpec_Name, -1, n.Name)
|
||||
}
|
||||
walk(v, edge.ImportSpec_Path, -1, n.Path)
|
||||
if n.Comment != nil {
|
||||
walk(v, edge.ImportSpec_Comment, -1, n.Comment)
|
||||
}
|
||||
|
||||
case *ast.ValueSpec:
|
||||
if n.Doc != nil {
|
||||
walk(v, edge.ValueSpec_Doc, -1, n.Doc)
|
||||
}
|
||||
walkList(v, edge.ValueSpec_Names, n.Names)
|
||||
if n.Type != nil {
|
||||
walk(v, edge.ValueSpec_Type, -1, n.Type)
|
||||
}
|
||||
walkList(v, edge.ValueSpec_Values, n.Values)
|
||||
if n.Comment != nil {
|
||||
walk(v, edge.ValueSpec_Comment, -1, n.Comment)
|
||||
}
|
||||
|
||||
case *ast.TypeSpec:
|
||||
if n.Doc != nil {
|
||||
walk(v, edge.TypeSpec_Doc, -1, n.Doc)
|
||||
}
|
||||
walk(v, edge.TypeSpec_Name, -1, n.Name)
|
||||
if n.TypeParams != nil {
|
||||
walk(v, edge.TypeSpec_TypeParams, -1, n.TypeParams)
|
||||
}
|
||||
walk(v, edge.TypeSpec_Type, -1, n.Type)
|
||||
if n.Comment != nil {
|
||||
walk(v, edge.TypeSpec_Comment, -1, n.Comment)
|
||||
}
|
||||
|
||||
case *ast.BadDecl:
|
||||
// nothing to do
|
||||
|
||||
case *ast.GenDecl:
|
||||
if n.Doc != nil {
|
||||
walk(v, edge.GenDecl_Doc, -1, n.Doc)
|
||||
}
|
||||
walkList(v, edge.GenDecl_Specs, n.Specs)
|
||||
|
||||
case *ast.FuncDecl:
|
||||
if n.Doc != nil {
|
||||
walk(v, edge.FuncDecl_Doc, -1, n.Doc)
|
||||
}
|
||||
if n.Recv != nil {
|
||||
walk(v, edge.FuncDecl_Recv, -1, n.Recv)
|
||||
}
|
||||
walk(v, edge.FuncDecl_Name, -1, n.Name)
|
||||
walk(v, edge.FuncDecl_Type, -1, n.Type)
|
||||
if n.Body != nil {
|
||||
walk(v, edge.FuncDecl_Body, -1, n.Body)
|
||||
}
|
||||
|
||||
case *ast.File:
|
||||
if n.Doc != nil {
|
||||
walk(v, edge.File_Doc, -1, n.Doc)
|
||||
}
|
||||
walk(v, edge.File_Name, -1, n.Name)
|
||||
walkList(v, edge.File_Decls, n.Decls)
|
||||
// don't walk n.Comments - they have been
|
||||
// visited already through the individual
|
||||
// nodes
|
||||
|
||||
default:
|
||||
// (includes *ast.Package)
|
||||
panic(fmt.Sprintf("Walk: unexpected node type %T", n))
|
||||
}
|
||||
|
||||
v.pop(node)
|
||||
}
|
||||
Reference in New Issue
Block a user