Update go version
This commit is contained in:
71
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/clone.go
vendored
Normal file
71
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/clone.go
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2023 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"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// CloneNode returns a deep copy of a Node.
|
||||
// It omits pointers to ast.{Scope,Object} variables.
|
||||
func CloneNode[T ast.Node](n T) T {
|
||||
return cloneNode(n).(T)
|
||||
}
|
||||
|
||||
func cloneNode(n ast.Node) ast.Node {
|
||||
var clone func(x reflect.Value) reflect.Value
|
||||
set := func(dst, src reflect.Value) {
|
||||
src = clone(src)
|
||||
if src.IsValid() {
|
||||
dst.Set(src)
|
||||
}
|
||||
}
|
||||
clone = func(x reflect.Value) reflect.Value {
|
||||
switch x.Kind() {
|
||||
case reflect.Pointer:
|
||||
if x.IsNil() {
|
||||
return x
|
||||
}
|
||||
// Skip fields of types potentially involved in cycles.
|
||||
switch x.Interface().(type) {
|
||||
case *ast.Object, *ast.Scope:
|
||||
return reflect.Zero(x.Type())
|
||||
}
|
||||
y := reflect.New(x.Type().Elem())
|
||||
set(y.Elem(), x.Elem())
|
||||
return y
|
||||
|
||||
case reflect.Struct:
|
||||
y := reflect.New(x.Type()).Elem()
|
||||
for i := 0; i < x.Type().NumField(); i++ {
|
||||
set(y.Field(i), x.Field(i))
|
||||
}
|
||||
return y
|
||||
|
||||
case reflect.Slice:
|
||||
if x.IsNil() {
|
||||
return x
|
||||
}
|
||||
y := reflect.MakeSlice(x.Type(), x.Len(), x.Cap())
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
set(y.Index(i), x.Index(i))
|
||||
}
|
||||
return y
|
||||
|
||||
case reflect.Interface:
|
||||
y := reflect.New(x.Type()).Elem()
|
||||
set(y, x.Elem())
|
||||
return y
|
||||
|
||||
case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer:
|
||||
panic(x) // unreachable in AST
|
||||
|
||||
default:
|
||||
return x // bool, string, number
|
||||
}
|
||||
}
|
||||
return clone(reflect.ValueOf(n)).Interface().(ast.Node)
|
||||
}
|
||||
143
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/comment.go
vendored
Normal file
143
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/comment.go
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
// 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 astutil
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"iter"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Deprecation returns the paragraph of the doc comment that starts with the
|
||||
// conventional "Deprecation: " marker, as defined by
|
||||
// https://go.dev/wiki/Deprecated, or "" if the documented symbol is not
|
||||
// deprecated.
|
||||
func Deprecation(doc *ast.CommentGroup) string {
|
||||
for p := range strings.SplitSeq(doc.Text(), "\n\n") {
|
||||
// There is still some ambiguity for deprecation message. This function
|
||||
// only returns the paragraph introduced by "Deprecated: ". More
|
||||
// information related to the deprecation may follow in additional
|
||||
// paragraphs, but the deprecation message should be able to stand on
|
||||
// its own. See golang/go#38743.
|
||||
if strings.HasPrefix(p, "Deprecated: ") {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// -- plundered from the future (CL 605517, issue #68021) --
|
||||
|
||||
// TODO(adonovan): replace with ast.Directive in go1.26 (#68021).
|
||||
// Beware of our local mods to handle analysistest
|
||||
// "want" comments on the same line.
|
||||
|
||||
// A directive is a comment line with special meaning to the Go
|
||||
// toolchain or another tool. It has the form:
|
||||
//
|
||||
// //tool:name args
|
||||
//
|
||||
// The "tool:" portion is missing for the three directives named
|
||||
// line, extern, and export.
|
||||
//
|
||||
// See https://go.dev/doc/comment#Syntax for details of Go comment
|
||||
// syntax and https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives
|
||||
// for details of directives used by the Go compiler.
|
||||
type Directive struct {
|
||||
Pos token.Pos // of preceding "//"
|
||||
Tool string
|
||||
Name string
|
||||
Args string // may contain internal spaces
|
||||
}
|
||||
|
||||
// isDirective reports whether c is a comment directive.
|
||||
// This code is also in go/printer.
|
||||
func isDirective(c string) bool {
|
||||
// "//line " is a line directive.
|
||||
// "//extern " is for gccgo.
|
||||
// "//export " is for cgo.
|
||||
// (The // has been removed.)
|
||||
if strings.HasPrefix(c, "line ") || strings.HasPrefix(c, "extern ") || strings.HasPrefix(c, "export ") {
|
||||
return true
|
||||
}
|
||||
|
||||
// "//[a-z0-9]+:[a-z0-9]"
|
||||
// (The // has been removed.)
|
||||
colon := strings.Index(c, ":")
|
||||
if colon <= 0 || colon+1 >= len(c) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i <= colon+1; i++ {
|
||||
if i == colon {
|
||||
continue
|
||||
}
|
||||
b := c[i]
|
||||
if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Directives returns the directives within the comment.
|
||||
func Directives(g *ast.CommentGroup) (res []*Directive) {
|
||||
if g != nil {
|
||||
// Avoid (*ast.CommentGroup).Text() as it swallows directives.
|
||||
for _, c := range g.List {
|
||||
if len(c.Text) > 2 &&
|
||||
c.Text[1] == '/' &&
|
||||
c.Text[2] != ' ' &&
|
||||
isDirective(c.Text[2:]) {
|
||||
|
||||
tool, nameargs, ok := strings.Cut(c.Text[2:], ":")
|
||||
if !ok {
|
||||
// Must be one of {line,extern,export}.
|
||||
tool, nameargs = "", tool
|
||||
}
|
||||
name, args, _ := strings.Cut(nameargs, " ") // tab??
|
||||
// Permit an additional line comment after the args, chiefly to support
|
||||
// [golang.org/x/tools/go/analysis/analysistest].
|
||||
args, _, _ = strings.Cut(args, "//")
|
||||
res = append(res, &Directive{
|
||||
Pos: c.Slash,
|
||||
Tool: tool,
|
||||
Name: name,
|
||||
Args: strings.TrimSpace(args),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Comments returns an iterator over the comments overlapping the specified interval.
|
||||
// Comments are sorted by position in the file, so we can use binary search.
|
||||
func Comments(file *ast.File, start, end token.Pos) iter.Seq[*ast.Comment] {
|
||||
return func(yield func(*ast.Comment) bool) {
|
||||
// Find the first comment group that overlaps the range.
|
||||
i := sort.Search(len(file.Comments), func(i int) bool {
|
||||
return file.Comments[i].End() >= start
|
||||
})
|
||||
for _, cg := range file.Comments[i:] {
|
||||
if cg.Pos() > end {
|
||||
return
|
||||
}
|
||||
// Find the first comment in the group that overlaps the range.
|
||||
j := sort.Search(len(cg.List), func(j int) bool {
|
||||
return cg.List[j].End() >= start
|
||||
})
|
||||
for _, co := range cg.List[j:] {
|
||||
if co.Pos() > end {
|
||||
return
|
||||
}
|
||||
if !yield(co) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/cursor.go
vendored
Normal file
38
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/cursor.go
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2026 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"
|
||||
|
||||
"golang.org/x/tools/go/ast/edge"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
// UnparenCursor returns the cursor for an expression with any
|
||||
// enclosing parentheses removed, similar to [ast.Unparen].
|
||||
// It is often prudent to call this before switching on the
|
||||
// type of cur.Node().
|
||||
//
|
||||
// See also [UnparenEnclosingCursor].
|
||||
func UnparenCursor(cur inspector.Cursor) inspector.Cursor {
|
||||
for is[*ast.ParenExpr](cur) {
|
||||
cur, _ = cur.FirstChild()
|
||||
}
|
||||
return cur
|
||||
}
|
||||
|
||||
// UnparenEnclosingCursor returns the first element of
|
||||
// the [Cursor.Enclosing] sequence that is not itself enclosed
|
||||
// in parens. It is often prudent to call this before switching on
|
||||
// cur.ParentEdge().
|
||||
//
|
||||
// See also [UnparenCursor].
|
||||
func UnparenEnclosingCursor(cur inspector.Cursor) inspector.Cursor {
|
||||
for cur.ParentEdgeKind() == edge.ParenExpr_X {
|
||||
cur = cur.Parent()
|
||||
}
|
||||
return cur
|
||||
}
|
||||
107
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/equal.go
vendored
Normal file
107
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/equal.go
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright 2023 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"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Equal reports whether two nodes are structurally equal,
|
||||
// ignoring fields of type [token.Pos], [ast.Object],
|
||||
// and [ast.Scope], and comments.
|
||||
//
|
||||
// The operands x and y may be nil.
|
||||
// A nil slice is not equal to an empty slice.
|
||||
//
|
||||
// The provided function determines whether two identifiers
|
||||
// should be considered identical.
|
||||
func Equal(x, y ast.Node, identical func(x, y *ast.Ident) bool) bool {
|
||||
if x == nil || y == nil {
|
||||
return x == y
|
||||
}
|
||||
return equal(reflect.ValueOf(x), reflect.ValueOf(y), identical)
|
||||
}
|
||||
|
||||
// EqualSyntax reports whether x and y are equal.
|
||||
// Identifiers are considered equal if they are spelled the same.
|
||||
// Comments are ignored.
|
||||
func EqualSyntax(x, y ast.Expr) bool {
|
||||
sameName := func(x, y *ast.Ident) bool { return x.Name == y.Name }
|
||||
return Equal(x, y, sameName)
|
||||
}
|
||||
|
||||
func equal(x, y reflect.Value, identical func(x, y *ast.Ident) bool) bool {
|
||||
// Ensure types are the same
|
||||
if x.Type() != y.Type() {
|
||||
return false
|
||||
}
|
||||
switch x.Kind() {
|
||||
case reflect.Pointer:
|
||||
if x.IsNil() || y.IsNil() {
|
||||
return x.IsNil() == y.IsNil()
|
||||
}
|
||||
switch t := x.Interface().(type) {
|
||||
// Skip fields of types potentially involved in cycles.
|
||||
case *ast.Object, *ast.Scope, *ast.CommentGroup:
|
||||
return true
|
||||
case *ast.Ident:
|
||||
return identical(t, y.Interface().(*ast.Ident))
|
||||
default:
|
||||
return equal(x.Elem(), y.Elem(), identical)
|
||||
}
|
||||
|
||||
case reflect.Interface:
|
||||
if x.IsNil() || y.IsNil() {
|
||||
return x.IsNil() == y.IsNil()
|
||||
}
|
||||
return equal(x.Elem(), y.Elem(), identical)
|
||||
|
||||
case reflect.Struct:
|
||||
for i := range x.NumField() {
|
||||
xf := x.Field(i)
|
||||
yf := y.Field(i)
|
||||
// Skip position fields.
|
||||
if xpos, ok := xf.Interface().(token.Pos); ok {
|
||||
ypos := yf.Interface().(token.Pos)
|
||||
// Numeric value of a Pos is not significant but its "zeroness" is,
|
||||
// because it is often significant, e.g. CallExpr.Variadic(Ellipsis), ChanType.Arrow.
|
||||
if xpos.IsValid() != ypos.IsValid() {
|
||||
return false
|
||||
}
|
||||
} else if !equal(xf, yf, identical) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case reflect.Slice:
|
||||
if x.IsNil() || y.IsNil() {
|
||||
return x.IsNil() == y.IsNil()
|
||||
}
|
||||
if x.Len() != y.Len() {
|
||||
return false
|
||||
}
|
||||
for i := range x.Len() {
|
||||
if !equal(x.Index(i), y.Index(i), identical) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case reflect.String:
|
||||
return x.String() == y.String()
|
||||
|
||||
case reflect.Bool:
|
||||
return x.Bool() == y.Bool()
|
||||
|
||||
case reflect.Int:
|
||||
return x.Int() == y.Int()
|
||||
|
||||
default:
|
||||
panic(x)
|
||||
}
|
||||
}
|
||||
35
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/fields.go
vendored
Normal file
35
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/fields.go
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
|
||||
package astutil
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"iter"
|
||||
)
|
||||
|
||||
// FlatFields 'flattens' an ast.FieldList, returning an iterator over each
|
||||
// (name, field) combination in the list. For unnamed fields, the identifier is
|
||||
// nil.
|
||||
func FlatFields(list *ast.FieldList) iter.Seq2[*ast.Ident, *ast.Field] {
|
||||
return func(yield func(*ast.Ident, *ast.Field) bool) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, field := range list.List {
|
||||
if len(field.Names) == 0 {
|
||||
if !yield(nil, field) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
for _, name := range field.Names {
|
||||
if !yield(name, field) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/purge.go
vendored
Normal file
93
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/purge.go
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2023 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 provides various AST utility functions for gopls.
|
||||
package astutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
// PurgeFuncBodies returns a copy of src in which the contents of each
|
||||
// outermost {...} region have been deleted, except for struct and
|
||||
// interface type bodies and the bodies of length-elided array
|
||||
// literals ([...]T), whose element count is part of the type. It
|
||||
// includes function bodies, function-literal bodies, and the bodies
|
||||
// of slice, map, and explicitly-sized array composite literals (whose
|
||||
// contents don't affect the type of the enclosing declaration). This
|
||||
// reduces the amount of work required to parse the top-level
|
||||
// declarations.
|
||||
//
|
||||
// PurgeFuncBodies does not preserve newlines or position information.
|
||||
// Also, if the input is invalid, parsing the output of
|
||||
// PurgeFuncBodies may result in a different tree due to its effects
|
||||
// on parser error recovery.
|
||||
func PurgeFuncBodies(src []byte) []byte {
|
||||
// Destroy the content of any {...}-bracketed regions that are
|
||||
// not immediately preceded by a "struct" or "interface" token,
|
||||
// and that are not the body of a length-elided array literal.
|
||||
// That includes function bodies, switch/select bodies, and most
|
||||
// composite literals; this will lead to non-void functions that
|
||||
// don't have return statements, which of course is a type error,
|
||||
// but that's ok.
|
||||
|
||||
var out bytes.Buffer
|
||||
file := token.NewFileSet().AddFile("", -1, len(src))
|
||||
var sc scanner.Scanner
|
||||
sc.Init(file, src, nil, 0)
|
||||
var prev token.Token
|
||||
var cursor int // last consumed src offset
|
||||
var braces []token.Pos // stack of unclosed braces, or -1 for a region we preserve
|
||||
var ellipsis bool // saw "[...]" not yet consumed by a literal-body "{"
|
||||
for {
|
||||
pos, tok, _ := sc.Scan()
|
||||
if tok == token.EOF {
|
||||
break
|
||||
}
|
||||
switch tok {
|
||||
case token.COMMENT:
|
||||
// TODO(adonovan): opt: skip, to save an estimated 20% of time.
|
||||
|
||||
case token.SEMICOLON:
|
||||
ellipsis = false
|
||||
|
||||
case token.RBRACK:
|
||||
// "...]" occurs only in the array-type prefix of a
|
||||
// composite literal; variadic "..." is followed by
|
||||
// a type or ")", never "]".
|
||||
if prev == token.ELLIPSIS {
|
||||
ellipsis = true
|
||||
}
|
||||
|
||||
case token.LBRACE:
|
||||
if prev == token.STRUCT || prev == token.INTERFACE {
|
||||
pos = -1 // type body: preserve (don't consume ellipsis)
|
||||
} else if ellipsis {
|
||||
pos = -1 // [...]T literal body: preserve
|
||||
ellipsis = false
|
||||
}
|
||||
braces = append(braces, pos)
|
||||
|
||||
case token.RBRACE:
|
||||
if last := len(braces) - 1; last >= 0 {
|
||||
top := braces[last]
|
||||
braces = braces[:last]
|
||||
if top < 0 {
|
||||
// preserve
|
||||
} else if len(braces) == 0 { // toplevel only
|
||||
// Delete {...} body.
|
||||
start := file.Offset(top)
|
||||
end := file.Offset(pos)
|
||||
out.Write(src[cursor : start+len("{")])
|
||||
cursor = end
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = tok
|
||||
}
|
||||
out.Write(src[cursor:])
|
||||
return out.Bytes()
|
||||
}
|
||||
103
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/stringlit.go
vendored
Normal file
103
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/stringlit.go
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// 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 astutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// RangeInStringLiteral calculates the positional range within a string literal
|
||||
// corresponding to the specified start and end byte offsets within the logical string.
|
||||
func RangeInStringLiteral(lit *ast.BasicLit, start, end int) (Range, error) {
|
||||
startPos, err := PosInStringLiteral(lit, start)
|
||||
if err != nil {
|
||||
return Range{}, fmt.Errorf("start: %v", err)
|
||||
}
|
||||
endPos, err := PosInStringLiteral(lit, end)
|
||||
if err != nil {
|
||||
return Range{}, fmt.Errorf("end: %v", err)
|
||||
}
|
||||
return Range{startPos, endPos}, nil
|
||||
}
|
||||
|
||||
// PosInStringLiteral returns the position within a string literal
|
||||
// corresponding to the specified byte offset within the logical
|
||||
// string that it denotes.
|
||||
func PosInStringLiteral(lit *ast.BasicLit, offset int) (token.Pos, error) {
|
||||
raw := lit.Value
|
||||
|
||||
value, err := strconv.Unquote(raw)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !(0 <= offset && offset <= len(value)) {
|
||||
return 0, fmt.Errorf("invalid offset")
|
||||
}
|
||||
|
||||
pos, _ := walkStringLiteral(lit, lit.End(), offset)
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// OffsetInStringLiteral returns the byte offset within the logical (unquoted)
|
||||
// string corresponding to the specified source position.
|
||||
func OffsetInStringLiteral(lit *ast.BasicLit, pos token.Pos) (int, error) {
|
||||
if !NodeContainsPos(lit, pos) {
|
||||
return 0, fmt.Errorf("invalid position")
|
||||
}
|
||||
|
||||
raw := lit.Value
|
||||
|
||||
value, err := strconv.Unquote(raw)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, offset := walkStringLiteral(lit, pos, len(value))
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// walkStringLiteral iterates through the raw string literal to map between
|
||||
// a file position and a logical byte offset. It stops when it reaches
|
||||
// either the targetPos or the targetOffset.
|
||||
//
|
||||
// TODO(hxjiang): consider making an iterator.
|
||||
func walkStringLiteral(lit *ast.BasicLit, targetPos token.Pos, targetOffset int) (token.Pos, int) {
|
||||
raw := lit.Value
|
||||
norm := int(lit.End()-lit.Pos()) > len(lit.Value)
|
||||
|
||||
// remove quotes
|
||||
quote := raw[0] // '"' or '`'
|
||||
raw = raw[1 : len(raw)-1]
|
||||
|
||||
var (
|
||||
i = 0 // byte index within logical value
|
||||
pos = lit.Pos() + 1 // position within literal
|
||||
)
|
||||
|
||||
for raw != "" {
|
||||
r, _, rest, _ := strconv.UnquoteChar(raw, quote) // can't fail
|
||||
sz := len(raw) - len(rest) // length of literal char in raw bytes
|
||||
|
||||
nextPos := pos + token.Pos(sz)
|
||||
if norm && r == '\n' {
|
||||
nextPos++
|
||||
}
|
||||
nextI := i + utf8.RuneLen(r) // length of logical char in "cooked" bytes
|
||||
|
||||
if nextPos > targetPos || nextI > targetOffset {
|
||||
break
|
||||
}
|
||||
|
||||
raw = raw[sz:]
|
||||
i = nextI
|
||||
pos = nextPos
|
||||
}
|
||||
|
||||
return pos, i
|
||||
}
|
||||
61
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/unpack.go
vendored
Normal file
61
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/unpack.go
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2023 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"
|
||||
|
||||
"honnef.co/go/tools/internal/xtools-internal/typeparams"
|
||||
)
|
||||
|
||||
// UnpackRecv unpacks a receiver type expression, reporting whether it is a
|
||||
// pointer receiver, along with the type name identifier and any receiver type
|
||||
// parameter identifiers.
|
||||
//
|
||||
// Copied (with modifications) from go/types.
|
||||
func UnpackRecv(rtyp ast.Expr) (ptr bool, rname *ast.Ident, tparams []*ast.Ident) {
|
||||
L: // unpack receiver type
|
||||
// This accepts invalid receivers such as ***T and does not
|
||||
// work for other invalid receivers, but we don't care. The
|
||||
// validity of receiver expressions is checked elsewhere.
|
||||
for {
|
||||
switch t := rtyp.(type) {
|
||||
case *ast.ParenExpr:
|
||||
rtyp = t.X
|
||||
case *ast.StarExpr:
|
||||
ptr = true
|
||||
rtyp = t.X
|
||||
default:
|
||||
break L
|
||||
}
|
||||
}
|
||||
|
||||
// unpack type parameters, if any
|
||||
switch rtyp.(type) {
|
||||
case *ast.IndexExpr, *ast.IndexListExpr:
|
||||
var indices []ast.Expr
|
||||
rtyp, _, indices, _ = typeparams.UnpackIndexExpr(rtyp)
|
||||
for _, arg := range indices {
|
||||
var par *ast.Ident
|
||||
switch arg := arg.(type) {
|
||||
case *ast.Ident:
|
||||
par = arg
|
||||
default:
|
||||
// ignore errors
|
||||
}
|
||||
if par == nil {
|
||||
par = &ast.Ident{NamePos: arg.Pos(), Name: "_"}
|
||||
}
|
||||
tparams = append(tparams, par)
|
||||
}
|
||||
}
|
||||
|
||||
// unpack receiver name
|
||||
if name, _ := rtyp.(*ast.Ident); name != nil {
|
||||
rname = name
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
261
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/util.go
vendored
Normal file
261
vendor/honnef.co/go/tools/internal/xtools-internal/astutil/util.go
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
// 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 astutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"honnef.co/go/tools/internal/xtools-internal/moreiters"
|
||||
)
|
||||
|
||||
// NodeContains reports whether the Pos/End range of node n encloses
|
||||
// the given range.
|
||||
//
|
||||
// It is inclusive of both end points, to allow hovering (etc) when
|
||||
// the cursor is immediately after a node.
|
||||
//
|
||||
// Like [NodeRange], it treats the range of an [ast.File] as the
|
||||
// file's complete extent.
|
||||
//
|
||||
// Precondition: n must not be nil.
|
||||
func NodeContains(n ast.Node, rng Range) bool {
|
||||
return NodeRange(n).Contains(rng)
|
||||
}
|
||||
|
||||
// NodeContainsPos reports whether the Pos/End range of node n encloses
|
||||
// the given pos.
|
||||
//
|
||||
// Like [NodeRange], it treats the range of an [ast.File] as the
|
||||
// file's complete extent.
|
||||
func NodeContainsPos(n ast.Node, pos token.Pos) bool {
|
||||
return NodeRange(n).ContainsPos(pos)
|
||||
}
|
||||
|
||||
// EnclosingFile returns the syntax tree for the file enclosing c.
|
||||
//
|
||||
// TODO(adonovan): promote this to a method of Cursor.
|
||||
func EnclosingFile(c inspector.Cursor) *ast.File {
|
||||
c, _ = moreiters.First(c.Enclosing((*ast.File)(nil)))
|
||||
return c.Node().(*ast.File)
|
||||
}
|
||||
|
||||
// DocComment returns the doc comment for a node, if any.
|
||||
func DocComment(n ast.Node) *ast.CommentGroup {
|
||||
switch n := n.(type) {
|
||||
case *ast.FuncDecl:
|
||||
return n.Doc
|
||||
case *ast.GenDecl:
|
||||
return n.Doc
|
||||
case *ast.ValueSpec:
|
||||
return n.Doc
|
||||
case *ast.TypeSpec:
|
||||
return n.Doc
|
||||
case *ast.File:
|
||||
return n.Doc
|
||||
case *ast.ImportSpec:
|
||||
return n.Doc
|
||||
case *ast.Field:
|
||||
return n.Doc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Format returns a string representation of the node n.
|
||||
func Format(fset *token.FileSet, n ast.Node) string {
|
||||
var buf strings.Builder
|
||||
printer.Fprint(&buf, fset, n) // ignore errors
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// -- Range --
|
||||
|
||||
// Range is a Pos interval.
|
||||
// It implements [analysis.Range] and [ast.Node].
|
||||
type Range struct{ Start, EndPos token.Pos }
|
||||
|
||||
// RangeOf constructs a Range.
|
||||
//
|
||||
// RangeOf exists to pacify the "unkeyed literal" (composites) vet
|
||||
// check. It would be nice if there were a way for a type to add
|
||||
// itself to the allowlist.
|
||||
func RangeOf(start, end token.Pos) Range { return Range{start, end} }
|
||||
|
||||
// NodeRange returns the extent of node n as a Range.
|
||||
//
|
||||
// For unfortunate historical reasons, the Pos/End extent of an
|
||||
// ast.File runs from the start of its package declaration---excluding
|
||||
// copyright comments, build tags, and package documentation---to the
|
||||
// end of its last declaration, excluding any trailing comments. So,
|
||||
// as a special case, if n is an [ast.File], NodeContains uses
|
||||
// n.FileStart <= pos && pos <= n.FileEnd to report whether the
|
||||
// position lies anywhere within the file.
|
||||
func NodeRange(n ast.Node) Range {
|
||||
if file, ok := n.(*ast.File); ok {
|
||||
return Range{file.FileStart, file.FileEnd} // entire file
|
||||
}
|
||||
return Range{n.Pos(), n.End()}
|
||||
}
|
||||
|
||||
func (r Range) Pos() token.Pos { return r.Start }
|
||||
func (r Range) End() token.Pos { return r.EndPos }
|
||||
|
||||
// ContainsPos reports whether the range (inclusive of both end points)
|
||||
// includes the specified position.
|
||||
func (r Range) ContainsPos(pos token.Pos) bool {
|
||||
return r.Contains(RangeOf(pos, pos))
|
||||
}
|
||||
|
||||
// Contains reports whether the range (inclusive of both end points)
|
||||
// includes the specified range.
|
||||
func (r Range) Contains(rng Range) bool {
|
||||
return r.Start <= rng.Start && rng.EndPos <= r.EndPos
|
||||
}
|
||||
|
||||
// IsValid reports whether the range is valid.
|
||||
func (r Range) IsValid() bool { return r.Start.IsValid() && r.Start <= r.EndPos }
|
||||
|
||||
// --
|
||||
|
||||
// Select returns the syntax nodes identified by a user's text
|
||||
// selection. It returns three nodes: the innermost node that wholly
|
||||
// encloses the selection; and the first and last nodes that are
|
||||
// wholly enclosed by the selection.
|
||||
//
|
||||
// For example, given this selection:
|
||||
//
|
||||
// { f(); g(); /* comment */ }
|
||||
// ~~~~~~~~~~~
|
||||
//
|
||||
// Select returns the enclosing BlockStmt, the f() CallExpr, and the g() CallExpr.
|
||||
//
|
||||
// If the selection does not wholly enclose any nodes, Select returns an error
|
||||
// and invalid start/end nodes, but it may return a valid enclosing node.
|
||||
//
|
||||
// Callers that require exactly one syntax tree (e.g. just f() or just
|
||||
// g()) should check that the returned start and end nodes are
|
||||
// identical.
|
||||
//
|
||||
// This function is intended to be called early in the handling of a
|
||||
// user's request, since it is tolerant of sloppy selection including
|
||||
// extraneous whitespace and comments. Use it in new code instead of
|
||||
// PathEnclosingInterval. When the exact extent of a node is known,
|
||||
// use [Cursor.FindByPos] instead.
|
||||
//
|
||||
// TODO(hxjiang): Consider refactoring the function signature. It is currently
|
||||
// confusing that an error is returned even when a valid enclosing node is
|
||||
// successfully found. Consider grouping all cursors into one struct.
|
||||
func Select(curFile inspector.Cursor, start, end token.Pos) (_enclosing, _start, _end inspector.Cursor, _ error) {
|
||||
curEnclosing, ok := curFile.FindByPos(start, end)
|
||||
if !ok {
|
||||
return noCursor, noCursor, noCursor, fmt.Errorf("invalid selection")
|
||||
}
|
||||
|
||||
// Find the first and last node wholly within the (start, end) range.
|
||||
// We'll narrow the effective selection to them, to exclude whitespace.
|
||||
// (This matches the functionality of PathEnclosingInterval.)
|
||||
var curStart, curEnd inspector.Cursor
|
||||
rng := RangeOf(start, end)
|
||||
for cur := range curEnclosing.Preorder() {
|
||||
if rng.Contains(NodeRange(cur.Node())) {
|
||||
// The start node has the least Pos.
|
||||
if !curStart.Valid() {
|
||||
curStart = cur
|
||||
}
|
||||
// The end node has the greatest End.
|
||||
// End positions do not change monotonically,
|
||||
// so we must compute the max.
|
||||
if !curEnd.Valid() ||
|
||||
cur.Node().End() > curEnd.Node().End() {
|
||||
curEnd = cur
|
||||
}
|
||||
}
|
||||
}
|
||||
if !curStart.Valid() {
|
||||
// The selection is valid (inside curEnclosing) but contains no
|
||||
// complete nodes. This happens for point selections (start == end),
|
||||
// or selections covering only only spaces, comments, and punctuation
|
||||
// tokens.
|
||||
// Return the enclosing node so the caller can still use the context.
|
||||
return curEnclosing, noCursor, noCursor, fmt.Errorf("invalid selection")
|
||||
}
|
||||
return curEnclosing, curStart, curEnd, nil
|
||||
}
|
||||
|
||||
var noCursor inspector.Cursor
|
||||
|
||||
// MaybeParenthesize returns new, possibly wrapped in parens if needed
|
||||
// to preserve operator precedence when it replaces old, whose parent
|
||||
// is parentNode.
|
||||
//
|
||||
// (This would be more naturally written in terms of Cursor, but one of
|
||||
// the callers--the inliner--does not have cursors handy.)
|
||||
func MaybeParenthesize(parentNode ast.Node, old, new ast.Expr) ast.Expr {
|
||||
if needsParens(parentNode, old, new) {
|
||||
new = &ast.ParenExpr{X: new}
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
||||
func needsParens(parentNode ast.Node, old, new ast.Expr) bool {
|
||||
// An expression beneath a non-expression
|
||||
// has no precedence ambiguity.
|
||||
parent, ok := parentNode.(ast.Expr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
precedence := func(n ast.Node) int {
|
||||
switch n := n.(type) {
|
||||
case *ast.UnaryExpr, *ast.StarExpr:
|
||||
return token.UnaryPrec
|
||||
case *ast.BinaryExpr:
|
||||
return n.Op.Precedence()
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Parens are not required if the new node
|
||||
// is not unary or binary.
|
||||
newprec := precedence(new)
|
||||
if newprec < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Parens are required if parent and child are both
|
||||
// unary or binary and the parent has higher precedence.
|
||||
if precedence(parent) > newprec {
|
||||
return true
|
||||
}
|
||||
|
||||
// Was the old node the operand of a postfix operator?
|
||||
// f().sel
|
||||
// f()[i:j]
|
||||
// f()[i]
|
||||
// f().(T)
|
||||
// f()(x)
|
||||
switch parent := parent.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
return parent.X == old
|
||||
case *ast.IndexExpr:
|
||||
return parent.X == old
|
||||
case *ast.SliceExpr:
|
||||
return parent.X == old
|
||||
case *ast.TypeAssertExpr:
|
||||
return parent.X == old
|
||||
case *ast.CallExpr:
|
||||
return parent.Fun == old
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func is[T any](n any) bool {
|
||||
_, ok := n.(T)
|
||||
return ok
|
||||
}
|
||||
Reference in New Issue
Block a user