| // Copyright 2022 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. | |
| //go:build go1.18 | |
| package main | |
| import ( | |
| "fmt" | |
| "go/ast" | |
| "go/importer" | |
| "go/parser" | |
| "go/token" | |
| "go/types" | |
| "log" | |
| ) | |
| const methods = ` | |
| //!+input | |
| package methods | |
| type List[E any] []E | |
| func (l List[_]) Len() int { | |
| return len(l) | |
| } | |
| func (l *List[E]) Append(v E) { | |
| *l = append(*l, v) | |
| } | |
| type Pair[L, R comparable] struct { | |
| left L | |
| right R | |
| } | |
| func (p Pair[L, _]) Left() L { | |
| return p.left | |
| } | |
| func (p Pair[_, R]) Right() R { | |
| return p.right | |
| } | |
| func (p Pair[L, R]) Equal(other Pair[L, R]) bool { | |
| return p.Left() == other.Left() && p.Right() == other.Right() | |
| } | |
| //!-input | |
| ` | |
| // !+describe | |
| func Describe(fset *token.FileSet, file *ast.File) error { | |
| conf := types.Config{Importer: importer.Default()} | |
| info := &types.Info{ | |
| Defs: make(map[*ast.Ident]types.Object), | |
| } | |
| pkg, err := conf.Check("pair", fset, []*ast.File{file}, info) | |
| if err != nil { | |
| return err | |
| } | |
| for _, name := range pkg.Scope().Names() { | |
| obj := pkg.Scope().Lookup(name) | |
| typ := obj.Type().(*types.Named) | |
| fmt.Printf("type %s has %d methods\n", name, typ.NumMethods()) | |
| for i := 0; i < typ.NumMethods(); i++ { | |
| m := typ.Method(i) | |
| sig := m.Type().(*types.Signature) | |
| recv := sig.Recv().Type() | |
| fmt.Printf(" %s has %d receiver type parameters\n", m.Name(), sig.RecvTypeParams().Len()) | |
| fmt.Printf(" ...and receiver type %+v\n", recv) | |
| } | |
| } | |
| // } | |
| // func foo() {} | |
| for _, decl := range file.Decls { | |
| fdecl, ok := decl.(*ast.FuncDecl) | |
| if !ok { | |
| continue | |
| } | |
| fmt.Printf("Declaration of %q has receiver node type %T\n", fdecl.Name, fdecl.Recv.List[0].Type) | |
| declObj := info.Defs[fdecl.Name] | |
| recvIdent := receiverTypeName(fdecl.Recv.List[0].Type) | |
| typ := pkg.Scope().Lookup(recvIdent.Name).Type().(*types.Named) | |
| // ptr := types.NewPointer(typ) | |
| name := fdecl.Name.Name | |
| methodObj, _, _ := types.LookupFieldOrMethod(typ, false, pkg, name) | |
| if declObj == methodObj { | |
| fmt.Printf(" info.Uses[%s] == types.LookupFieldOrMethod(%s, ..., %q)\n", fdecl.Name, typ, name) | |
| } | |
| } | |
| return nil | |
| } | |
| func receiverTypeName(e ast.Expr) *ast.Ident { | |
| if s, ok := e.(*ast.StarExpr); ok { | |
| e = s.X | |
| } | |
| switch e := e.(type) { | |
| case *ast.IndexExpr: | |
| return e.X.(*ast.Ident) | |
| case *ast.IndexListExpr: | |
| return e.X.(*ast.Ident) | |
| } | |
| panic("unexpected receiver node type") | |
| } | |
| //!-describe | |
| /* | |
| //!+output | |
| > go run golang.org/x/tools/internal/typeparams/example/methoddecls | |
| Pair has 2 methods | |
| Left has 2 receiver type parameters | |
| ...and receiver type pair.Pair[L, _] | |
| Right has 2 receiver type parameters | |
| ...and receiver type pair.Pair[_, R] | |
| Declaration of "Left" has receiver node type *ast.IndexListExpr | |
| info.Uses[Left] == types.LookupFieldOrMethod(pair.Pair[L, R any], ..., "Left") | |
| Declaration of "Right" has receiver node type *ast.IndexListExpr | |
| info.Uses[Right] == types.LookupFieldOrMethod(pair.Pair[L, R any], ..., "Right") | |
| //!-output | |
| */ | |
| func main() { | |
| // Parse one file. | |
| fset := token.NewFileSet() | |
| f, err := parser.ParseFile(fset, "methods.go", methods, 0) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| if err := Describe(fset, f); err != nil { | |
| log.Fatal(err) | |
| } | |
| } |