// MethodSet returns the method set of type T. It is thread-safe. // // If cache is nil, this function is equivalent to types.NewMethodSet(T). // Utility functions can thus expose an optional *MethodSetCache // parameter to clients that care about performance. // func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet { if cache == nil { return types.NewMethodSet(T) } cache.mu.Lock() defer cache.mu.Unlock() switch T := T.(type) { case *types.Named: return cache.lookupNamed(T).value case *types.Pointer: if N, ok := T.Elem().(*types.Named); ok { return cache.lookupNamed(N).pointer } } // all other types // (The map uses pointer equivalence, not type identity.) mset := cache.others[T] if mset == nil { mset = types.NewMethodSet(T) if cache.others == nil { cache.others = make(map[types.Type]*types.MethodSet) } cache.others[T] = mset } return mset }
// lockPath returns a typePath describing the location of a lock value // contained in typ. If there is no contained lock, it returns nil. func lockPath(tpkg *types.Package, typ types.Type) typePath { if typ == nil { return nil } // We're only interested in the case in which the underlying // type is a struct. (Interfaces and pointers are safe to copy.) styp, ok := typ.Underlying().(*types.Struct) if !ok { return nil } // We're looking for cases in which a reference to this type // can be locked, but a value cannot. This differentiates // embedded interfaces from embedded values. if plock := types.NewMethodSet(types.NewPointer(typ)).Lookup(tpkg, "Lock"); plock != nil { if lock := types.NewMethodSet(typ).Lookup(tpkg, "Lock"); lock == nil { return []types.Type{typ} } } nfields := styp.NumFields() for i := 0; i < nfields; i++ { ftyp := styp.Field(i).Type() subpath := lockPath(tpkg, ftyp) if subpath != nil { return append(subpath, typ) } } return nil }
func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } { if cache.named == nil { cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet }) } // Avoid recomputing mset(*T) for each distinct Pointer // instance whose underlying type is a named type. msets, ok := cache.named[named] if !ok { msets.value = types.NewMethodSet(named) msets.pointer = types.NewMethodSet(types.NewPointer(named)) cache.named[named] = msets } return msets }
//!+ func PrintSkeleton(pkg *types.Package, ifacename, concname string) error { obj := pkg.Scope().Lookup(ifacename) if obj == nil { return fmt.Errorf("%s.%s not found", pkg.Path(), ifacename) } if _, ok := obj.(*types.TypeName); !ok { return fmt.Errorf("%v is not a named type", obj) } iface, ok := obj.Type().Underlying().(*types.Interface) if !ok { return fmt.Errorf("type %v is a %T, not an interface", obj, obj.Type().Underlying()) } // Use first letter of type name as receiver parameter. if !isValidIdentifier(concname) { return fmt.Errorf("invalid concrete type name: %q", concname) } r, _ := utf8.DecodeRuneInString(concname) fmt.Printf("// *%s implements %s.%s.\n", concname, pkg.Path(), ifacename) fmt.Printf("type %s struct{}\n", concname) mset := types.NewMethodSet(iface) for i := 0; i < mset.Len(); i++ { meth := mset.At(i).Obj() sig := types.TypeString(meth.Type(), (*types.Package).Name) fmt.Printf("func (%c *%s) %s%s {\n\tpanic(\"unimplemented\")\n}\n", r, concname, meth.Name(), strings.TrimPrefix(sig, "func")) } return nil }
func makeIfaceSummary(iface *types.Interface) ifaceSummary { summary := ifaceSummary{ iface: iface, implementable: true, } methodset := types.NewMethodSet(iface) for i := 0; i < methodset.Len(); i++ { obj := methodset.At(i).Obj() if !obj.Exported() { summary.implementable = false continue } m, ok := obj.(*types.Func) if !ok { log.Panicf("unexpected methodset obj: %s (%T)", obj, obj) } if !isImplementable(m.Type().(*types.Signature)) { summary.implementable = false } if isCallable(m) { summary.callable = append(summary.callable, m) } } return summary }
func getMethods(pkg *types.Package, typename string) map[string]*types.Func { r := make(map[string]*types.Func) mset := types.NewMethodSet(types.NewPointer(pkg.Scope().Lookup(typename).Type())) for i := 0; i < mset.Len(); i++ { fn := mset.At(i).Obj().(*types.Func) r[fn.Name()] = fn } return r }
// combinedMethodSet returns the method set for a named type T // merged with all the methods of *T that have different names than // the methods of T. // // combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet // but doesn't require a MethodSetCache. // TODO(gri) If this functionality doesn't change over time, consider // just calling IntuitiveMethodSet eventually. func combinedMethodSet(T *types.Named) []*types.Selection { // method set for T mset := types.NewMethodSet(T) var res []*types.Selection for i, n := 0, mset.Len(); i < n; i++ { res = append(res, mset.At(i)) } // add all *T methods with names different from T methods pmset := types.NewMethodSet(types.NewPointer(T)) for i, n := 0, pmset.Len(); i < n; i++ { pm := pmset.At(i) if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil { res = append(res, pm) } } return res }
func (w *Walker) emitType(obj *types.TypeName) { name := obj.Name() typ := obj.Type() switch typ := typ.Underlying().(type) { case *types.Struct: w.emitStructType(name, typ) case *types.Interface: w.emitIfaceType(name, typ) return // methods are handled by emitIfaceType default: w.emitf("type %s %s", name, w.typeString(typ.Underlying())) } // emit methods with value receiver var methodNames map[string]bool vset := types.NewMethodSet(typ) for i, n := 0, vset.Len(); i < n; i++ { m := vset.At(i) if m.Obj().Exported() { w.emitMethod(m) if methodNames == nil { methodNames = make(map[string]bool) } methodNames[m.Obj().Name()] = true } } // emit methods with pointer receiver; exclude // methods that we have emitted already // (the method set of *T includes the methods of T) pset := types.NewMethodSet(types.NewPointer(typ)) for i, n := 0, pset.Len(); i < n; i++ { m := pset.At(i) if m.Obj().Exported() && !methodNames[m.Obj().Name()] { w.emitMethod(m) } } }
func exportedMethodSet(T types.Type) []*types.Func { var methods []*types.Func methodset := types.NewMethodSet(T) for i := 0; i < methodset.Len(); i++ { obj := methodset.At(i).Obj() if !obj.Exported() { continue } switch obj := obj.(type) { case *types.Func: methods = append(methods, obj) default: log.Panicf("unexpected methodset obj: %s", obj) } } return methods }
// ExampleMethodSet prints the method sets of various types. func ExampleMethodSet() { // Parse a single source file. const input = ` package temperature import "fmt" type Celsius float64 func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } func (c *Celsius) SetF(f float64) { *c = Celsius(f - 32 / 9 * 5) } ` fset := token.NewFileSet() f, err := parser.ParseFile(fset, "celsius.go", input, 0) if err != nil { log.Fatal(err) } // Type-check a package consisting of this file. // Type information for the imported packages // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. conf := types.Config{Importer: importer.Default()} pkg, err := conf.Check("temperature", fset, []*ast.File{f}, nil) if err != nil { log.Fatal(err) } // Print the method sets of Celsius and *Celsius. celsius := pkg.Scope().Lookup("Celsius").Type() for _, t := range []types.Type{celsius, types.NewPointer(celsius)} { fmt.Printf("Method set of %s:\n", t) mset := types.NewMethodSet(t) for i := 0; i < mset.Len(); i++ { fmt.Println(mset.At(i)) } fmt.Println() } // Output: // Method set of temperature.Celsius: // method (temperature.Celsius) String() string // // Method set of *temperature.Celsius: // method (*temperature.Celsius) SetF(f float64) // method (*temperature.Celsius) String() string }
func methodDocComment(prog *loader.Program, tname *types.TypeName, methodName string) (string, error) { t := tname.Type() if !types.IsInterface(t) { // Use the pointer type to get as many methods as possible. t = types.NewPointer(t) } mset := types.NewMethodSet(t) sel := mset.Lookup(nil, methodName) if sel == nil { return "", errgo.Newf("cannot find method %v on %v", methodName, t) } obj := sel.Obj() decl, err := findDecl(prog, obj.Pos()) if err != nil { return "", errgo.Mask(err) } switch decl := decl.(type) { case *ast.GenDecl: if decl.Tok != token.TYPE { return "", errgo.Newf("found non-type decl %#v", decl) } for _, spec := range decl.Specs { tspec := spec.(*ast.TypeSpec) it := tspec.Type.(*ast.InterfaceType) for _, m := range it.Methods.List { for _, id := range m.Names { if id.Pos() == obj.Pos() { return m.Doc.Text(), nil } } } } return "", errgo.Newf("method definition not found in type") case *ast.FuncDecl: if decl.Name.Pos() != obj.Pos() { return "", errgo.Newf("method definition not found (at %#v)", prog.Fset.Position(obj.Pos())) } return decl.Doc.Text(), nil default: return "", errgo.Newf("unexpected declaration %T found", decl) } }
func (w *Walker) emitIfaceType(name string, typ *types.Interface) { pop := w.pushScope("type " + name + " interface") var methodNames []string complete := true mset := types.NewMethodSet(typ) for i, n := 0, mset.Len(); i < n; i++ { m := mset.At(i).Obj().(*types.Func) if !m.Exported() { complete = false continue } methodNames = append(methodNames, m.Name()) w.emitf("%s%s", m.Name(), w.signatureString(m.Type().(*types.Signature))) } if !complete { // The method set has unexported methods, so all the // implementations are provided by the same package, // so the method set can be extended. Instead of recording // the full set of names (below), record only that there were // unexported methods. (If the interface shrinks, we will notice // because a method signature emitted during the last loop // will disappear.) w.emitf("unexported methods") } pop() if !complete { return } if len(methodNames) == 0 { w.emitf("type %s interface {}", name) return } sort.Strings(methodNames) w.emitf("type %s interface { %s }", name, strings.Join(methodNames, ", ")) }
func exportedMethodSet(T types.Type) []*types.Func { var methods []*types.Func methodset := types.NewMethodSet(T) for i := 0; i < methodset.Len(); i++ { obj := methodset.At(i).Obj() if !obj.Exported() { continue } // Skip methods from the embedded classes, so that // only methods that are implemented in Go are included. if pref := pkgFirstElem(obj.Pkg()); pref == "Java" || pref == "ObjC" { continue } switch obj := obj.(type) { case *types.Func: methods = append(methods, obj) default: log.Panicf("unexpected methodset obj: %s", obj) } } return methods }
// Smoke test to ensure that imported methods get the correct package. func TestCorrectMethodPackage(t *testing.T) { skipSpecialPlatforms(t) // This package only handles gc export data. if runtime.Compiler != "gc" { t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) return } imports := make(map[string]*types.Package) _, err := Import(imports, "net/http") if err != nil { t.Fatal(err) } mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type() mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex sel := mset.Lookup(nil, "Lock") lock := sel.Obj().(*types.Func) if got, want := lock.Pkg().Path(), "sync"; got != want { t.Errorf("got package path %q; want %q", got, want) } }
// process collects informations about a go package. func (p *Package) process() error { var err error funcs := make(map[string]Func) structs := make(map[string]Struct) scope := p.pkg.Scope() for _, name := range scope.Names() { obj := scope.Lookup(name) if !obj.Exported() { continue } p.n++ p.syms.addSymbol(obj) } for _, name := range scope.Names() { obj := scope.Lookup(name) if !obj.Exported() { continue } switch obj := obj.(type) { case *types.Const: p.addConst(obj) case *types.Var: p.addVar(obj) case *types.Func: funcs[name], err = newFuncFrom(p, "", obj, obj.Type().(*types.Signature)) if err != nil { return err } case *types.TypeName: named := obj.Type().(*types.Named) switch typ := named.Underlying().(type) { case *types.Struct: structs[name], err = newStruct(p, obj) if err != nil { return err } case *types.Basic: // ok. handled by p.syms-types case *types.Array: // ok. handled by p.syms-types case *types.Interface: // ok. handled by p.syms-types case *types.Signature: // ok. handled by p.syms-types case *types.Slice: // ok. handled by p.syms-types default: //TODO(sbinet) panic(fmt.Errorf("not yet supported: %v (%T)", typ, obj)) } default: //TODO(sbinet) panic(fmt.Errorf("not yet supported: %v (%T)", obj, obj)) } } // remove ctors from funcs. // add methods. for sname, s := range structs { for name, fct := range funcs { if fct.Return() == nil { continue } if fct.Return() == s.GoType() { delete(funcs, name) fct.doc = p.getDoc(sname, scope.Lookup(name)) fct.ctor = true s.ctors = append(s.ctors, fct) structs[sname] = s } } ptyp := types.NewPointer(s.GoType()) p.syms.addType(nil, ptyp) mset := types.NewMethodSet(ptyp) for i := 0; i < mset.Len(); i++ { meth := mset.At(i) if !meth.Obj().Exported() { continue } m, err := newFuncFrom(p, sname, meth.Obj(), meth.Type().(*types.Signature)) if err != nil { return err } s.meths = append(s.meths, m) if isStringer(meth.Obj()) { s.prots |= ProtoStringer } } p.addStruct(s) } for _, fct := range funcs { p.addFunc(fct) } // attach docstrings to methods for _, n := range p.syms.names() { sym := p.syms.syms[n] if !sym.isNamed() { continue } switch typ := sym.GoType().(type) { case *types.Named: for i := 0; i < typ.NumMethods(); i++ { m := typ.Method(i) if !m.Exported() { continue } doc := p.getDoc(sym.goname, m) mname := types.ObjectString(m, nil) msym := p.syms.sym(mname) if msym == nil { panic(fmt.Errorf( "gopy: could not retrieve symbol for %q", m.FullName(), )) } msym.doc = doc } } } return err }
// Implements displays the "implements" relation as it pertains to the // selected type. // If the selection is a method, 'implements' displays // the corresponding methods of the types that would have been reported // by an implements query on the receiver type. // func implements(q *Query) error { lconf := loader.Config{Build: q.Build} allowErrors(&lconf) qpkg, err := importQueryPackage(q.Pos, &lconf) if err != nil { return err } // Set the packages to search. if len(q.Scope) > 0 { // Inspect all packages in the analysis scope, if specified. if err := setPTAScope(&lconf, q.Scope); err != nil { return err } } else { // Otherwise inspect the forward and reverse // transitive closure of the selected package. // (In theory even this is incomplete.) _, rev, _ := importgraph.Build(q.Build) for path := range rev.Search(qpkg) { lconf.ImportWithTests(path) } // TODO(adonovan): for completeness, we should also // type-check and inspect function bodies in all // imported packages. This would be expensive, but we // could optimize by skipping functions that do not // contain type declarations. This would require // changing the loader's TypeCheckFuncBodies hook to // provide the []*ast.File. } // Load/parse/type-check the program. lprog, err := lconf.Load() if err != nil { return err } q.Fset = lprog.Fset qpos, err := parseQueryPos(lprog, q.Pos, false) if err != nil { return err } // Find the selected type. path, action := findInterestingNode(qpos.info, qpos.path) var method *types.Func var T types.Type // selected type (receiver if method != nil) switch action { case actionExpr: // method? if id, ok := path[0].(*ast.Ident); ok { if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok { recv := obj.Type().(*types.Signature).Recv() if recv == nil { return fmt.Errorf("this function is not a method") } method = obj T = recv.Type() } } case actionType: T = qpos.info.TypeOf(path[0].(ast.Expr)) } if T == nil { return fmt.Errorf("no type or method here") } // Find all named types, even local types (which can have // methods via promotion) and the built-in "error". var allNamed []types.Type for _, info := range lprog.AllPackages { for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok { allNamed = append(allNamed, obj.Type()) } } } allNamed = append(allNamed, types.Universe.Lookup("error").Type()) var msets typeutil.MethodSetCache // Test each named type. var to, from, fromPtr []types.Type for _, U := range allNamed { if isInterface(T) { if msets.MethodSet(T).Len() == 0 { continue // empty interface } if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T interface, U interface if !types.Identical(T, U) { if types.AssignableTo(U, T) { to = append(to, U) } if types.AssignableTo(T, U) { from = append(from, U) } } } else { // T interface, U concrete if types.AssignableTo(U, T) { to = append(to, U) } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) { to = append(to, pU) } } } else if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T concrete, U interface if types.AssignableTo(T, U) { from = append(from, U) } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) { fromPtr = append(fromPtr, U) } } } var pos interface{} = qpos if nt, ok := deref(T).(*types.Named); ok { pos = nt.Obj() } // Sort types (arbitrarily) to ensure test determinism. sort.Sort(typesByString(to)) sort.Sort(typesByString(from)) sort.Sort(typesByString(fromPtr)) var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils if method != nil { for _, t := range to { toMethod = append(toMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } for _, t := range from { fromMethod = append(fromMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } for _, t := range fromPtr { fromPtrMethod = append(fromPtrMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } } q.result = &implementsResult{ qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod, } return nil }
func (r *implementsResult) display(printf printfFunc) { relation := "is implemented by" meth := func(sel *types.Selection) { if sel != nil { printf(sel.Obj(), "\t%s method (%s).%s", relation, r.qpos.typeString(sel.Recv()), sel.Obj().Name()) } } if isInterface(r.t) { if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset printf(r.pos, "empty interface type %s", r.qpos.typeString(r.t)) return } if r.method == nil { printf(r.pos, "interface type %s", r.qpos.typeString(r.t)) } else { printf(r.method, "abstract method %s", r.qpos.objectString(r.method)) } // Show concrete types (or methods) first; use two passes. for i, sub := range r.to { if !isInterface(sub) { if r.method == nil { printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s", relation, typeKind(sub), r.qpos.typeString(sub)) } else { meth(r.toMethod[i]) } } } for i, sub := range r.to { if isInterface(sub) { if r.method == nil { printf(sub.(*types.Named).Obj(), "\t%s %s type %s", relation, typeKind(sub), r.qpos.typeString(sub)) } else { meth(r.toMethod[i]) } } } relation = "implements" for i, super := range r.from { if r.method == nil { printf(super.(*types.Named).Obj(), "\t%s %s", relation, r.qpos.typeString(super)) } else { meth(r.fromMethod[i]) } } } else { relation = "implements" if r.from != nil { if r.method == nil { printf(r.pos, "%s type %s", typeKind(r.t), r.qpos.typeString(r.t)) } else { printf(r.method, "concrete method %s", r.qpos.objectString(r.method)) } for i, super := range r.from { if r.method == nil { printf(super.(*types.Named).Obj(), "\t%s %s", relation, r.qpos.typeString(super)) } else { meth(r.fromMethod[i]) } } } if r.fromPtr != nil { if r.method == nil { printf(r.pos, "pointer type *%s", r.qpos.typeString(r.t)) } else { // TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f. printf(r.method, "concrete method %s", r.qpos.objectString(r.method)) } for i, psuper := range r.fromPtr { if r.method == nil { printf(psuper.(*types.Named).Obj(), "\t%s %s", relation, r.qpos.typeString(psuper)) } else { meth(r.fromPtrMethod[i]) } } } else if r.from == nil { printf(r.pos, "%s type %s implements only interface{}", typeKind(r.t), r.qpos.typeString(r.t)) } } }