blob: b9cf9a03c1d2e4d8ad71b77af30a7326bc1387ea [file] [log] [blame]
// 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"
"strings"
"golang.org/x/tools/internal/typeparams"
)
// UnpackRecv unpacks a receiver type expression, reporting whether it is a
// pointer recever, 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
}
// NodeContains returns true if a node encloses a given position pos.
// The end point will also be inclusive, which will to allow hovering when the
// cursor is behind some nodes.
//
// Precondition: n must not be nil.
func NodeContains(n ast.Node, pos token.Pos) bool {
return n.Pos() <= pos && pos <= n.End()
}
// IsGenerated check if a file is generated code
func IsGenerated(file *ast.File) bool {
// TODO: replace this implementation with calling function ast.IsGenerated when go1.21 is assured
for _, group := range file.Comments {
for _, comment := range group.List {
if comment.Pos() > file.Package {
break // after package declaration
}
// opt: check Contains first to avoid unnecessary array allocation in Split.
const prefix = "// Code generated "
if strings.Contains(comment.Text, prefix) {
for _, line := range strings.Split(comment.Text, "\n") {
if strings.HasPrefix(line, prefix) && strings.HasSuffix(line, " DO NOT EDIT.") {
return true
}
}
}
}
}
return false
}