| // 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 |
| } |