// 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 }
// 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 }
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 FindAllExports(pkg *types.Package, fset *token.FileSet) []UnexportCandidate { candidates := []UnexportCandidate{} for _, name := range pkg.Scope().Names() { obj := pkg.Scope().Lookup(name) if !obj.Exported() { continue } displayName := obj.Name() if _, ok := obj.(*types.Func); ok { displayName += "()" } candidate := UnexportCandidate{obj.Name(), displayName, fset.Position(obj.Pos())} candidates = append(candidates, candidate) if tn, ok := obj.(*types.TypeName); ok { if str, ok := tn.Type().Underlying().(*types.Struct); ok { candidates = append(candidates, findStructFields(str, obj.Name(), fset)...) } ptrType := types.NewPointer(tn.Type()) methodSet := types.NewMethodSet(ptrType) for i := 0; i < methodSet.Len(); i++ { methodSel := methodSet.At(i) method := methodSel.Obj() // skip unexported functions, and functions from embedded fields. // The best I can figure out for embedded functions is if the selection index path is longer than 1. if !method.Exported() || len(methodSel.Index()) > 1 { continue } candidate := UnexportCandidate{method.Name(), obj.Name() + "." + method.Name() + "()", fset.Position(method.Pos())} candidates = append(candidates, candidate) } } } return candidates }
// 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 (p *Processor) isInitPresent(t types.Type) bool { ms := types.NewMethodSet(types.NewPointer(t)) for i := 0; i < ms.Len(); i++ { m := ms.At(i) if m.Obj().Name() == "Init" { return true } } return false }
func MustGetMethodListFromGoTypes(typ types.Type) (output []*types.Selection) { methodSet := types.NewMethodSet(typ) num := methodSet.Len() if num == 0 { return nil } output = make([]*types.Selection, num) for i := range output { output[i] = methodSet.At(i) } return output }
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 }
func TestRpcDemo(ot *testing.T) { importPath := "github.com/bronze1man/kmg/kmgGoSource" kmgCmd.MustRun("kmg go install " + importPath) typ := MustGetGoTypesFromReflect(reflect.TypeOf(&RpcDemo{})) typ1, ok := typ.(*types.Pointer) kmgTest.Equal(ok, true) typ2, ok := typ1.Elem().(*types.Named) kmgTest.Equal(ok, true) kmgTest.Equal(typ2.NumMethods(), 1) obj3 := typ2.Method(0) kmgTest.Equal(obj3.Name(), "PostScoreInt") typ4, ok := obj3.Type().(*types.Signature) kmgTest.Equal(ok, true) kmgTest.Equal(typ4.Params().Len(), 3) kmgTest.Equal(typ4.Results().Len(), 2) for _, testCase := range []struct { Type types.Type Expect string }{ {typ4.Params().At(0).Type(), "string"}, {typ4.Params().At(1).Type(), "int"}, {typ4.Params().At(2).Type(), "*RpcDemo"}, {typ4.Results().At(0).Type(), "string"}, {typ4.Results().At(1).Type(), "error"}, } { typS, importPathList := MustWriteGoTypes("github.com/bronze1man/kmg/kmgGoSource", testCase.Type) kmgTest.Equal(typS, testCase.Expect) kmgTest.Equal(len(importPathList), 0) } typS, importPathList := MustWriteGoTypes("github.com/bronze1man/kmg/kmgTest", typ4.Params().At(2).Type()) kmgTest.Equal(typS, "*kmgGoSource.RpcDemo") kmgTest.Equal(importPathList, []string{"github.com/bronze1man/kmg/kmgGoSource"}) methodSet := types.NewMethodSet(typ) kmgTest.Equal(methodSet.Len(), 1) kmgTest.Equal(methodSet.At(0).Obj().Name(), "PostScoreInt") }
// Smoke test to ensure that imported methods get the correct package. func TestCorrectMethodPackage(t *testing.T) { // This package does not handle gccgo export data. if runtime.Compiler == "gccgo" { 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) } }
func (r *implementsResult) display(printf printfFunc) { if isInterface(r.t) { if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset printf(r.pos, "empty interface type %s", r.t) return } printf(r.pos, "interface type %s", r.t) // Show concrete types first; use two passes. for _, sub := range r.to { if !isInterface(sub) { printf(deref(sub).(*types.Named).Obj(), "\tis implemented by %s type %s", typeKind(sub), sub) } } for _, sub := range r.to { if isInterface(sub) { printf(deref(sub).(*types.Named).Obj(), "\tis implemented by %s type %s", typeKind(sub), sub) } } for _, super := range r.from { printf(super.(*types.Named).Obj(), "\timplements %s", super) } } else { if r.from != nil { printf(r.pos, "%s type %s", typeKind(r.t), r.t) for _, super := range r.from { printf(super.(*types.Named).Obj(), "\timplements %s", super) } } if r.fromPtr != nil { printf(r.pos, "pointer type *%s", r.t) for _, psuper := range r.fromPtr { printf(psuper.(*types.Named).Obj(), "\timplements %s", psuper) } } else if r.from == nil { printf(r.pos, "%s type %s implements only interface{}", typeKind(r.t), r.t) } } }
// 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) } }
func serverMethods(serverPkg, serverType string) ([]method, []string, error) { cfg := loader.Config{ TypeCheckFuncBodies: func(string) bool { return false }, ImportPkgs: map[string]bool{ serverPkg: false, // false means don't load tests. }, ParserMode: parser.ParseComments, } prog, err := cfg.Load() if err != nil { return nil, nil, errgo.Notef(err, "cannot load %q", serverPkg) } pkgInfo := prog.Imported[serverPkg] if pkgInfo == nil { return nil, nil, errgo.Newf("cannot find %q in imported code", serverPkg) } pkg := pkgInfo.Pkg obj := pkg.Scope().Lookup(serverType) if obj == nil { return nil, nil, errgo.Newf("type %s not found in %s", serverType, serverPkg) } objTypeName, ok := obj.(*types.TypeName) if !ok { return nil, nil, errgo.Newf("%s is not a type", serverType) } // Use the pointer type to get as many methods as possible. ptrObjType := types.NewPointer(objTypeName.Type()) imports := map[string]string{ "github.com/juju/httprequest": "httprequest", } var methods []method mset := types.NewMethodSet(ptrObjType) for i := 0; i < mset.Len(); i++ { sel := mset.At(i) if !sel.Obj().Exported() { continue } name := sel.Obj().Name() if name == "Close" { continue } ptype, rtype, err := parseMethodType(sel.Type().(*types.Signature)) if err != nil { fmt.Fprintf(os.Stderr, "ignoring method %s: %v\n", name, err) continue } comment := docComment(prog, sel) methods = append(methods, method{ Name: name, Doc: comment, ParamType: typeStr(ptype, imports), RespType: typeStr(rtype, imports), }) } var allImports []string for path := range imports { allImports = append(allImports, path) } return methods, allImports, nil }
// 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 }
// 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(o *Oracle, qpos *QueryPos) (queryResult, error) { // 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 nil, 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 nil, 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". // // TODO(adonovan): include all packages in PTA scope too? // i.e. don't reduceScope? // var allNamed []types.Type for _, info := range o.typeInfo { 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 types.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())) } } return &implementsResult{qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod}, 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.t) return } if r.method == nil { printf(r.pos, "interface type %s", 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), 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), 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, 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.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, super) } else { meth(r.fromMethod[i]) } } } if r.fromPtr != nil { if r.method == nil { printf(r.pos, "pointer type *%s", 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, psuper) } else { meth(r.fromPtrMethod[i]) } } } else if r.from == nil { printf(r.pos, "%s type %s implements only interface{}", typeKind(r.t), r.t) } } }
// 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 }
// Visit implements the ast.Visitor interface. func (f *File) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.GenDecl: // Variables, constants, types. for _, spec := range n.Specs { switch spec := spec.(type) { case *ast.ValueSpec: if constantFlag && n.Tok == token.CONST || variableFlag && n.Tok == token.VAR { for _, ident := range spec.Names { if f.match(ident.Name) { f.printNode(n, ident, f.nameURL(ident.Name)) break } } } case *ast.TypeSpec: // If there is only one Spec, there are probably no parens and the // comment we want appears before the type keyword, bound to // the GenDecl. If the Specs are parenthesized, the comment we want // is bound to the Spec. Hence we dig into the GenDecl to the Spec, // but only if there are no parens. node := ast.Node(n) if n.Lparen.IsValid() { node = spec } if f.match(spec.Name.Name) { if typeFlag { f.printNode(node, spec.Name, f.nameURL(spec.Name.Name)) } else { switch spec.Type.(type) { case *ast.InterfaceType: if interfaceFlag { f.printNode(node, spec.Name, f.nameURL(spec.Name.Name)) } case *ast.StructType: if structFlag { f.printNode(node, spec.Name, f.nameURL(spec.Name.Name)) } } } if f.doPrint && f.defs[spec.Name] != nil && f.defs[spec.Name].Type() != nil { ms := types.NewMethodSet(f.defs[spec.Name].Type()) //.Type().MethodSet() if ms.Len() == 0 { ms = types.NewMethodSet(types.NewPointer(f.defs[spec.Name].Type())) //.MethodSet() } f.methodSet(ms) } } case *ast.ImportSpec: continue // Don't care. } } case *ast.FuncDecl: // Methods, top-level functions. if f.match(n.Name.Name) { n.Body = nil // Do not print the function body. if methodFlag && n.Recv != nil { f.printNode(n, n.Name, f.methodURL(n.Recv.List[0].Type, n.Name.Name)) } else if functionFlag && n.Recv == nil { f.printNode(n, n.Name, f.nameURL(n.Name.Name)) } } } return f }