func accessibleMethods(t types.Type, from *types.Package) []*types.Selection { var methods []*types.Selection for _, meth := range typeutil.IntuitiveMethodSet(t, nil) { if isAccessibleFrom(meth.Obj(), from) { methods = append(methods, meth) } } return methods }
func TestIntuitiveMethodSet(t *testing.T) { const source = ` package P type A int func (A) f() func (*A) g() ` fset := token.NewFileSet() f, err := parser.ParseFile(fset, "hello.go", source, 0) if err != nil { t.Fatal(err) } var conf types.Config pkg, err := conf.Check("P", fset, []*ast.File{f}, nil) if err != nil { t.Fatal(err) } qual := types.RelativeTo(pkg) for _, test := range []struct { expr string // type expression want string // intuitive method set }{ {"A", "(A).f (*A).g"}, {"*A", "(*A).f (*A).g"}, {"error", "(error).Error"}, {"*error", ""}, {"struct{A}", "(struct{A}).f (*struct{A}).g"}, {"*struct{A}", "(*struct{A}).f (*struct{A}).g"}, } { tv, err := types.Eval(fset, pkg, 0, test.expr) if err != nil { t.Errorf("Eval(%s) failed: %v", test.expr, err) } var names []string for _, m := range typeutil.IntuitiveMethodSet(tv.Type, nil) { name := fmt.Sprintf("(%s).%s", types.TypeString(m.Recv(), qual), m.Obj().Name()) names = append(names, name) } got := strings.Join(names, " ") if got != test.want { t.Errorf("IntuitiveMethodSet(%s) = %q, want %q", test.expr, got, test.want) } } }
func main() { if len(os.Args) != 3 { log.Fatal("Usage: doc <package> <object>") } //!+part1 pkgpath, name := os.Args[1], os.Args[2] // The loader loads a complete Go program from source code. conf := loader.Config{ParserMode: parser.ParseComments} conf.Import(pkgpath) lprog, err := conf.Load() if err != nil { log.Fatal(err) // load error } // Find the package and package-level object. pkg := lprog.Package(pkgpath).Pkg obj := pkg.Scope().Lookup(name) if obj == nil { log.Fatalf("%s.%s not found", pkg.Path(), name) } //!-part1 //!+part2 // Print the object and its methods (incl. location of definition). fmt.Println(obj) for _, sel := range typeutil.IntuitiveMethodSet(obj.Type(), nil) { fmt.Printf("%s: %s\n", lprog.Fset.Position(sel.Obj().Pos()), sel) } // Find the path from the root of the AST to the object's position. // Walk up to the enclosing ast.Decl for the doc comment. _, path, _ := lprog.PathEnclosingInterval(obj.Pos(), obj.Pos()) for _, n := range path { switch n := n.(type) { case *ast.GenDecl: fmt.Println("\n", n.Doc.Text()) return case *ast.FuncDecl: fmt.Println("\n", n.Doc.Text()) return } } //!-part2 }
// WritePackage writes to buf a human-readable summary of p. func WritePackage(buf *bytes.Buffer, p *Package) { fmt.Fprintf(buf, "%s:\n", p) var names []string maxname := 0 for name := range p.Members { if l := len(name); l > maxname { maxname = l } names = append(names, name) } from := p.Pkg sort.Strings(names) for _, name := range names { switch mem := p.Members[name].(type) { case *NamedConst: fmt.Fprintf(buf, " const %-*s %s = %s\n", maxname, name, mem.Name(), mem.Value.RelString(from)) case *Function: fmt.Fprintf(buf, " func %-*s %s\n", maxname, name, relType(mem.Type(), from)) case *Type: fmt.Fprintf(buf, " type %-*s %s\n", maxname, name, relType(mem.Type().Underlying(), from)) for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) { fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from))) } case *Global: fmt.Fprintf(buf, " var %-*s %s\n", maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from)) } } fmt.Fprintf(buf, "\n") }
func (a *analysis) namedType(obj *types.TypeName, implements map[*types.Named]implementsFacts) { qualifier := types.RelativeTo(obj.Pkg()) T := obj.Type().(*types.Named) v := &TypeInfoJSON{ Name: obj.Name(), Size: sizes.Sizeof(T), Align: sizes.Alignof(T), Methods: []anchorJSON{}, // (JS wants non-nil) } // addFact adds the fact "is implemented by T" (by) or // "implements T" (!by) to group. addFact := func(group *implGroupJSON, T types.Type, by bool) { Tobj := deref(T).(*types.Named).Obj() var byKind string if by { // Show underlying kind of implementing type, // e.g. "slice", "array", "struct". s := reflect.TypeOf(T.Underlying()).String() byKind = strings.ToLower(strings.TrimPrefix(s, "*types.")) } group.Facts = append(group.Facts, implFactJSON{ ByKind: byKind, Other: anchorJSON{ Href: a.posURL(Tobj.Pos(), len(Tobj.Name())), Text: types.TypeString(T, qualifier), }, }) } // IMPLEMENTS if r, ok := implements[T]; ok { if isInterface(T) { // "T is implemented by <conc>" ... // "T is implemented by <iface>"... // "T implements <iface>"... group := implGroupJSON{ Descr: types.TypeString(T, qualifier), } // Show concrete types first; use two passes. for _, sub := range r.to { if !isInterface(sub) { addFact(&group, sub, true) } } for _, sub := range r.to { if isInterface(sub) { addFact(&group, sub, true) } } for _, super := range r.from { addFact(&group, super, false) } v.ImplGroups = append(v.ImplGroups, group) } else { // T is concrete. if r.from != nil { // "T implements <iface>"... group := implGroupJSON{ Descr: types.TypeString(T, qualifier), } for _, super := range r.from { addFact(&group, super, false) } v.ImplGroups = append(v.ImplGroups, group) } if r.fromPtr != nil { // "*C implements <iface>"... group := implGroupJSON{ Descr: "*" + types.TypeString(T, qualifier), } for _, psuper := range r.fromPtr { addFact(&group, psuper, false) } v.ImplGroups = append(v.ImplGroups, group) } } } // METHOD SETS for _, sel := range typeutil.IntuitiveMethodSet(T, &a.prog.MethodSets) { meth := sel.Obj().(*types.Func) pos := meth.Pos() // may be 0 for error.Error v.Methods = append(v.Methods, anchorJSON{ Href: a.posURL(pos, len(meth.Name())), Text: types.SelectionString(sel, qualifier), }) } // Since there can be many specs per decl, we // can't attach the link to the keyword 'type' // (as we do with 'func'); we use the Ident. fi, offset := a.fileAndOffset(obj.Pos()) fi.addLink(aLink{ start: offset, end: offset + len(obj.Name()), title: fmt.Sprintf("type info for %s", obj.Name()), onclick: fmt.Sprintf("onClickTypeInfo(%d)", fi.addData(v)), }) // Add info for exported package-level types to the package info. if obj.Exported() && isPackageLevel(obj) { // TODO(adonovan): Path is not unique! // It is possible to declare a non-test package called x_test. a.result.pkgInfo(obj.Pkg().Path()).addType(v) } }