Update go version
This commit is contained in:
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