Skip to content

Commit

Permalink
internal/typesinternal: add ReceiverNamed helper
Browse files Browse the repository at this point in the history
...and factor numerous places to use it.

(This pattern kept recurring during my types.Alias audit,
golang/go#65294.)

Change-Id: I93228b735f7a8ff70df5c998017437d43742d9f3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/565075
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Tim King <taking@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
  • Loading branch information
adonovan authored and gopherbot committed Feb 21, 2024
1 parent a821e61 commit c111c4d
Show file tree
Hide file tree
Showing 18 changed files with 91 additions and 131 deletions.
9 changes: 3 additions & 6 deletions cmd/deadcode/deadcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
)

//go:embed doc.go
Expand Down Expand Up @@ -385,11 +385,8 @@ func prettyName(fn *ssa.Function, qualified bool) string {

// method receiver?
if recv := fn.Signature.Recv(); recv != nil {
t := recv.Type()
if ptr, ok := aliases.Unalias(t).(*types.Pointer); ok {
t = ptr.Elem()
}
buf.WriteString(aliases.Unalias(t).(*types.Named).Obj().Name())
_, named := typesinternal.ReceiverNamed(recv)
buf.WriteString(named.Obj().Name())
buf.WriteByte('.')
}

Expand Down
7 changes: 2 additions & 5 deletions cmd/guru/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typesinternal"
)

// describe describes the syntax node denoted by the query position,
Expand Down Expand Up @@ -911,11 +912,7 @@ func accessibleFields(recv types.Type, from *types.Package) []describeField {

// Handle recursion through anonymous fields.
if f.Anonymous() {
tf := f.Type()
if ptr, ok := tf.(*types.Pointer); ok {
tf = ptr.Elem()
}
if named, ok := tf.(*types.Named); ok { // (be defensive)
if _, named := typesinternal.ReceiverNamed(f); named != nil {
// If we've already visited this named type
// on this path, break the cycle.
for _, x := range stack {
Expand Down
8 changes: 3 additions & 5 deletions go/analysis/passes/httpmux/httpmux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typesinternal"
)

const Doc = `report using Go 1.22 enhanced ServeMux patterns in older Go versions
Expand Down Expand Up @@ -83,11 +84,8 @@ func isServeMuxRegisterCall(pass *analysis.Pass, call *ast.CallExpr) bool {
if !isMethodNamed(fn, "net/http", "Handle", "HandleFunc") {
return false
}
t, ok := fn.Type().(*types.Signature).Recv().Type().(*types.Pointer)
if !ok {
return false
}
return analysisutil.IsNamedType(t.Elem(), "net/http", "ServeMux")
isPtr, named := typesinternal.ReceiverNamed(fn.Type().(*types.Signature).Recv())
return isPtr && analysisutil.IsNamedType(named, "net/http", "ServeMux")
}

func isMethodNamed(f *types.Func, pkgPath string, names ...string) bool {
Expand Down
4 changes: 3 additions & 1 deletion go/analysis/passes/httpresponse/httpresponse.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/internal/typesinternal"
)

const Doc = `check for mistakes using HTTP responses
Expand Down Expand Up @@ -116,7 +117,8 @@ func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool {
if res.Len() != 2 {
return false // the function called does not return two values.
}
if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !analysisutil.IsNamedType(ptr.Elem(), "net/http", "Response") {
isPtr, named := typesinternal.ReceiverNamed(res.At(0))
if !isPtr || !analysisutil.IsNamedType(named, "net/http", "Response") {
return false // the first return type is not *http.Response.
}

Expand Down
8 changes: 3 additions & 5 deletions go/analysis/passes/loopclosure/loopclosure.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typesinternal"
"golang.org/x/tools/internal/versions"
)

Expand Down Expand Up @@ -367,9 +368,6 @@ func isMethodCall(info *types.Info, expr ast.Expr, pkgPath, typeName, method str

// Check that the receiver is a <pkgPath>.<typeName> or
// *<pkgPath>.<typeName>.
rtype := recv.Type()
if ptr, ok := recv.Type().(*types.Pointer); ok {
rtype = ptr.Elem()
}
return analysisutil.IsNamedType(rtype, pkgPath, typeName)
_, named := typesinternal.ReceiverNamed(recv)
return analysisutil.IsNamedType(named, pkgPath, typeName)
}
26 changes: 9 additions & 17 deletions go/analysis/passes/slog/slog.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typesinternal"
)

//go:embed doc.go
Expand Down Expand Up @@ -150,14 +151,10 @@ func isAttr(t types.Type) bool {
func shortName(fn *types.Func) string {
var r string
if recv := fn.Type().(*types.Signature).Recv(); recv != nil {
t := recv.Type()
if pt, ok := t.(*types.Pointer); ok {
t = pt.Elem()
}
if nt, ok := t.(*types.Named); ok {
r = nt.Obj().Name()
if _, named := typesinternal.ReceiverNamed(recv); named != nil {
r = named.Obj().Name()
} else {
r = recv.Type().String()
r = recv.Type().String() // anon struct/interface
}
r += "."
}
Expand All @@ -173,17 +170,12 @@ func kvFuncSkipArgs(fn *types.Func) (int, bool) {
return 0, false
}
var recvName string // by default a slog package function
recv := fn.Type().(*types.Signature).Recv()
if recv != nil {
t := recv.Type()
if pt, ok := t.(*types.Pointer); ok {
t = pt.Elem()
}
if nt, ok := t.(*types.Named); !ok {
return 0, false
} else {
recvName = nt.Obj().Name()
if recv := fn.Type().(*types.Signature).Recv(); recv != nil {
_, named := typesinternal.ReceiverNamed(recv)
if named == nil {
return 0, false // anon struct/interface
}
recvName = named.Obj().Name()
}
skip, ok := kvFuncs[recvName][fn.Name()]
return skip, ok
Expand Down
9 changes: 3 additions & 6 deletions go/analysis/passes/unmarshal/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typesinternal"
)

//go:embed doc.go
Expand Down Expand Up @@ -69,12 +70,8 @@ func run(pass *analysis.Pass) (interface{}, error) {
// (*"encoding/json".Decoder).Decode
// (* "encoding/gob".Decoder).Decode
// (* "encoding/xml".Decoder).Decode
t := recv.Type()
if ptr, ok := t.(*types.Pointer); ok {
t = ptr.Elem()
}
tname := t.(*types.Named).Obj()
if tname.Name() == "Decoder" {
_, named := typesinternal.ReceiverNamed(recv)
if tname := named.Obj(); tname.Name() == "Decoder" {
switch tname.Pkg().Path() {
case "encoding/json", "encoding/xml", "encoding/gob":
argidx = 0 // func(interface{})
Expand Down
8 changes: 6 additions & 2 deletions go/analysis/passes/unusedwrite/unusedwrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"golang.org/x/tools/go/analysis/passes/buildssa"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/internal/aliases"
)

//go:embed doc.go
Expand Down Expand Up @@ -159,10 +160,13 @@ func hasStructOrArrayType(v ssa.Value) bool {
//
// For example, for struct T {x int, y int), getFieldName(*T, 1) returns "y".
func getFieldName(tp types.Type, index int) string {
if pt, ok := tp.(*types.Pointer); ok {
// TODO(adonovan): use
// stp, ok := typeparams.Deref(tp).Underlying().(*types.Struct); ok {
// when Deref is defined.
if pt, ok := aliases.Unalias(tp).(*types.Pointer); ok {
tp = pt.Elem()
}
if named, ok := tp.(*types.Named); ok {
if named, ok := aliases.Unalias(tp).(*types.Named); ok {
tp = named.Underlying()
}
if stp, ok := tp.(*types.Struct); ok {
Expand Down
19 changes: 8 additions & 11 deletions go/ssa/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
)

//// Sanity checking utilities
Expand Down Expand Up @@ -180,17 +181,13 @@ func makeLen(T types.Type) *Builtin {
}
}

// receiverTypeArgs returns the type arguments to a function's receiver.
// Returns an empty list if obj does not have a receiver or its receiver does not have type arguments.
func receiverTypeArgs(obj *types.Func) []types.Type {
rtype := recvType(obj)
if rtype == nil {
return nil
}
rtype, _ = deptr(rtype)
named, ok := rtype.(*types.Named)
if !ok {
return nil
// receiverTypeArgs returns the type arguments to a method's receiver.
// Returns an empty list if the receiver does not have type arguments.
func receiverTypeArgs(method *types.Func) []types.Type {
recv := method.Type().(*types.Signature).Recv()
_, named := typesinternal.ReceiverNamed(recv)
if named == nil {
return nil // recv is anonymous struct/interface
}
ts := named.TypeArgs()
if ts.Len() == 0 {
Expand Down
10 changes: 3 additions & 7 deletions go/types/objectpath/objectpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"strings"

"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
)

// A Path is an opaque name that identifies a types.Object
Expand Down Expand Up @@ -395,13 +396,8 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
return "", false
}

recvT := meth.Type().(*types.Signature).Recv().Type()
if ptr, ok := recvT.(*types.Pointer); ok {
recvT = ptr.Elem()
}

named, ok := recvT.(*types.Named)
if !ok {
_, named := typesinternal.ReceiverNamed(meth.Type().(*types.Signature).Recv())
if named == nil {
return "", false
}

Expand Down
9 changes: 3 additions & 6 deletions gopls/internal/golang/code_lens.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/protocol"
"golang.org/x/tools/gopls/internal/protocol/command"
"golang.org/x/tools/internal/typesinternal"
)

type LensFunc func(context.Context, *cache.Snapshot, file.Handle) ([]protocol.CodeLens, error)
Expand Down Expand Up @@ -152,12 +153,8 @@ func matchTestFunc(fn *ast.FuncDecl, pkg *cache.Package, nameRe *regexp.Regexp,
}

// Check the type of the only parameter
paramTyp, ok := sig.Params().At(0).Type().(*types.Pointer)
if !ok {
return false
}
named, ok := paramTyp.Elem().(*types.Named)
if !ok {
isptr, named := typesinternal.ReceiverNamed(sig.Params().At(0))
if !isptr || named == nil {
return false
}
namedObj := named.Obj()
Expand Down
7 changes: 2 additions & 5 deletions gopls/internal/golang/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"golang.org/x/tools/gopls/internal/util/typesutil"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/tokeninternal"
"golang.org/x/tools/internal/typesinternal"
)

// hoverJSON contains the structured result of a hover query. It is
Expand Down Expand Up @@ -1235,11 +1236,7 @@ func promotedFields(t types.Type, from *types.Package) []promotedField {

// Handle recursion through anonymous fields.
if f.Anonymous() {
tf := f.Type()
if ptr, ok := tf.(*types.Pointer); ok {
tf = ptr.Elem()
}
if named, ok := tf.(*types.Named); ok { // (be defensive)
if _, named := typesinternal.ReceiverNamed(f); named != nil {
// If we've already visited this named type
// on this path, break the cycle.
for _, x := range stack {
Expand Down
9 changes: 3 additions & 6 deletions gopls/internal/golang/rename.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import (
"golang.org/x/tools/internal/diff"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
"golang.org/x/tools/refactor/satisfy"
)

Expand Down Expand Up @@ -1189,12 +1190,8 @@ func (r *renamer) updateCommentDocLinks() (map[protocol.DocumentURI][]diff.Edit,
if recv == nil {
continue
}
recvT := recv.Type()
if ptr, ok := recvT.(*types.Pointer); ok {
recvT = ptr.Elem()
}
named, isNamed := recvT.(*types.Named)
if !isNamed {
_, named := typesinternal.ReceiverNamed(recv)
if named == nil {
continue
}
// Doc links can't reference interface methods.
Expand Down
31 changes: 9 additions & 22 deletions internal/apidiff/compatibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"reflect"

"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
)

func (d *differ) checkCompatible(otn *types.TypeName, old, new types.Type) {
Expand Down Expand Up @@ -305,7 +306,8 @@ func (d *differ) checkMethodSet(otn *types.TypeName, oldt, newt types.Type, addc
// T and one for the embedded type U. We want both messages to appear,
// but the messageSet dedup logic will allow only one message for a given
// object. So use the part string to distinguish them.
if receiverNamedType(oldMethod).Obj() != otn {
recv := oldMethod.Type().(*types.Signature).Recv()
if _, named := typesinternal.ReceiverNamed(recv); named.Obj() != otn {
part = fmt.Sprintf(", method set of %s", msname)
}
d.incompatible(oldMethod, part, "removed")
Expand Down Expand Up @@ -336,34 +338,19 @@ func (d *differ) checkMethodSet(otn *types.TypeName, oldt, newt types.Type, addc
}

// exportedMethods collects all the exported methods of type's method set.
func exportedMethods(t types.Type) map[string]types.Object {
m := map[string]types.Object{}
func exportedMethods(t types.Type) map[string]*types.Func {
m := make(map[string]*types.Func)
ms := types.NewMethodSet(t)
for i := 0; i < ms.Len(); i++ {
obj := ms.At(i).Obj()
obj := ms.At(i).Obj().(*types.Func)
if obj.Exported() {
m[obj.Name()] = obj
}
}
return m
}

func receiverType(method types.Object) types.Type {
return method.Type().(*types.Signature).Recv().Type()
}

func receiverNamedType(method types.Object) *types.Named {
switch t := aliases.Unalias(receiverType(method)).(type) {
case *types.Pointer:
return aliases.Unalias(t.Elem()).(*types.Named)
case *types.Named:
return t
default:
panic("unreachable")
}
}

func hasPointerReceiver(method types.Object) bool {
_, ok := aliases.Unalias(receiverType(method)).(*types.Pointer)
return ok
func hasPointerReceiver(method *types.Func) bool {
isptr, _ := typesinternal.ReceiverNamed(method.Type().(*types.Signature).Recv())
return isptr
}
7 changes: 0 additions & 7 deletions internal/gcimporter/gcimporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,6 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
return
}

func deref(typ types.Type) types.Type {
if p, _ := typ.(*types.Pointer); p != nil {
return p.Elem()
}
return typ
}

type byPath []*types.Package

func (a byPath) Len() int { return len(a) }
Expand Down
Loading

0 comments on commit c111c4d

Please sign in to comment.