| // Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package types |
| |
| import ( |
| "bytes" |
| "fmt" |
| "go/ast" |
| "go/build" |
| "go/parser" |
| "go/printer" |
| "go/token" |
| "io/ioutil" |
| "log" |
| "os" |
| "path/filepath" |
| "regexp" |
| "sort" |
| "strconv" |
| "strings" |
| "time" |
| |
| "github.com/visualfc/gotools/command" |
| "github.com/visualfc/gotools/stdlib" |
| "golang.org/x/tools/go/buildutil" |
| "golang.org/x/tools/go/gcimporter" |
| "golang.org/x/tools/go/types" |
| ) |
| |
| var Command = &command.Command{ |
| Run: runTypes, |
| UsageLine: "types", |
| Short: "golang type util", |
| Long: `golang type util`, |
| } |
| |
| var ( |
| typesVerbose bool |
| typesAllowBinary bool |
| typesFilePos string |
| typesFileStdin bool |
| typesFindUse bool |
| typesFindDef bool |
| typesFindUseAll bool |
| typesFindInfo bool |
| typesFindDoc bool |
| ) |
| |
| //func init |
| func init() { |
| Command.Flag.BoolVar(&typesVerbose, "v", false, "verbose debugging") |
| Command.Flag.BoolVar(&typesAllowBinary, "b", false, "import can be satisfied by a compiled package object without corresponding sources.") |
| Command.Flag.StringVar(&typesFilePos, "pos", "", "file position \"file.go:pos\"") |
| Command.Flag.BoolVar(&typesFileStdin, "stdin", false, "input file use stdin") |
| Command.Flag.BoolVar(&typesFindInfo, "info", false, "find cursor info") |
| Command.Flag.BoolVar(&typesFindDef, "def", false, "find cursor define") |
| Command.Flag.BoolVar(&typesFindUse, "use", false, "find cursor usages") |
| Command.Flag.BoolVar(&typesFindUseAll, "all", false, "find cursor all usages in GOPATH") |
| Command.Flag.BoolVar(&typesFindDoc, "doc", false, "find cursor def doc") |
| } |
| |
| type ObjKind int |
| |
| const ( |
| ObjNone ObjKind = iota |
| ObjPkgName |
| ObjTypeName |
| ObjInterface |
| ObjStruct |
| ObjConst |
| ObjVar |
| ObjField |
| ObjFunc |
| ObjMethod |
| ObjLabel |
| ObjBuiltin |
| ObjNil |
| ) |
| |
| var ObjKindName = []string{"none", "package", |
| "type", "interface", "struct", |
| "const", "var", "field", |
| "func", "method", |
| "label", "builtin", "nil"} |
| |
| func (k ObjKind) String() string { |
| if k >= 0 && int(k) < len(ObjKindName) { |
| return ObjKindName[k] |
| } |
| return "unkwnown" |
| } |
| |
| var builtinInfoMap = map[string]string{ |
| "append": "func append(slice []Type, elems ...Type) []Type", |
| "copy": "func copy(dst, src []Type) int", |
| "delete": "func delete(m map[Type]Type1, key Type)", |
| "len": "func len(v Type) int", |
| "cap": "func cap(v Type) int", |
| "make": "func make(Type, size IntegerType) Type", |
| "new": "func new(Type) *Type", |
| "complex": "func complex(r, i FloatType) ComplexType", |
| "real": "func real(c ComplexType) FloatType", |
| "imag": "func imag(c ComplexType) FloatType", |
| "close": "func close(c chan<- Type)", |
| "panic": "func panic(v interface{})", |
| "recover": "func recover() interface{}", |
| "print": "func print(args ...Type)", |
| "println": "func println(args ...Type)", |
| "error": "type error interface {Error() string}", |
| } |
| |
| func builtinInfo(id string) string { |
| if info, ok := builtinInfoMap[id]; ok { |
| return "builtin " + info |
| } |
| return "builtin " + id |
| } |
| |
| func simpleObjInfo(obj types.Object) string { |
| pkg := obj.Pkg() |
| s := simpleType(obj.String()) |
| if pkg != nil && pkg.Name() == "main" { |
| return strings.Replace(s, simpleType(pkg.Path())+".", "", -1) |
| } |
| return s |
| } |
| |
| func simpleType(src string) string { |
| re, _ := regexp.Compile("[\\w\\./]+") |
| return re.ReplaceAllStringFunc(src, func(s string) string { |
| r := s |
| if i := strings.LastIndex(s, "/"); i != -1 { |
| r = s[i+1:] |
| } |
| if strings.Count(r, ".") > 1 { |
| r = r[strings.Index(r, ".")+1:] |
| } |
| return r |
| }) |
| } |
| |
| func runTypes(cmd *command.Command, args []string) error { |
| if len(args) < 1 { |
| cmd.Usage() |
| return nil |
| } |
| if typesVerbose { |
| now := time.Now() |
| defer func() { |
| log.Println("time", time.Now().Sub(now)) |
| }() |
| } |
| w := NewPkgWalker(&build.Default) |
| var cursor *FileCursor |
| if typesFilePos != "" { |
| var cursorInfo FileCursor |
| pos := strings.Index(typesFilePos, ":") |
| if pos != -1 { |
| cursorInfo.fileName = typesFilePos[:pos] |
| if i, err := strconv.Atoi(typesFilePos[pos+1:]); err == nil { |
| cursorInfo.cursorPos = i |
| } |
| } |
| if typesFileStdin { |
| src, err := ioutil.ReadAll(os.Stdin) |
| if err == nil { |
| cursorInfo.src = src |
| } |
| } |
| cursor = &cursorInfo |
| } |
| for _, pkgName := range args { |
| if pkgName == "." { |
| pkgPath, err := os.Getwd() |
| if err != nil { |
| log.Fatalln(err) |
| } |
| pkgName = pkgPath |
| } |
| conf := &PkgConfig{IgnoreFuncBodies: true, AllowBinary: true, WithTestFiles: true} |
| if cursor != nil { |
| conf.Cursor = cursor |
| conf.IgnoreFuncBodies = false |
| conf.Info = &types.Info{ |
| Uses: make(map[*ast.Ident]types.Object), |
| Defs: make(map[*ast.Ident]types.Object), |
| Selections: make(map[*ast.SelectorExpr]*types.Selection), |
| //Types: make(map[ast.Expr]types.TypeAndValue), |
| //Scopes : make(map[ast.Node]*types.Scope) |
| //Implicits : make(map[ast.Node]types.Object) |
| } |
| conf.XInfo = &types.Info{ |
| Uses: make(map[*ast.Ident]types.Object), |
| Defs: make(map[*ast.Ident]types.Object), |
| Selections: make(map[*ast.SelectorExpr]*types.Selection), |
| } |
| } |
| pkg, err := w.Import("", pkgName, conf) |
| if pkg == nil { |
| log.Fatalln("error import path", err) |
| } |
| if cursor != nil && (typesFindInfo || typesFindDef || typesFindUse) { |
| w.LookupCursor(pkg, conf, cursor) |
| } |
| } |
| return nil |
| } |
| |
| type FileCursor struct { |
| pkg string |
| fileName string |
| fileDir string |
| cursorPos int |
| pos token.Pos |
| src interface{} |
| xtest bool |
| } |
| |
| type PkgConfig struct { |
| IgnoreFuncBodies bool |
| AllowBinary bool |
| WithTestFiles bool |
| Cursor *FileCursor |
| Pkg *types.Package |
| XPkg *types.Package |
| Info *types.Info |
| XInfo *types.Info |
| Files map[string]*ast.File |
| TestFiles map[string]*ast.File |
| XTestFiles map[string]*ast.File |
| } |
| |
| func NewPkgWalker(context *build.Context) *PkgWalker { |
| return &PkgWalker{ |
| context: context, |
| fset: token.NewFileSet(), |
| parsedFileCache: map[string]*ast.File{}, |
| imported: map[string]*types.Package{"unsafe": types.Unsafe}, |
| gcimporter: map[string]*types.Package{"unsafe": types.Unsafe}, |
| } |
| } |
| |
| type PkgWalker struct { |
| fset *token.FileSet |
| context *build.Context |
| current *types.Package |
| importing types.Package |
| parsedFileCache map[string]*ast.File |
| imported map[string]*types.Package // packages already imported |
| gcimporter map[string]*types.Package |
| } |
| |
| func contains(list []string, s string) bool { |
| for _, t := range list { |
| if t == s { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func (w *PkgWalker) isBinaryPkg(pkg string) bool { |
| return stdlib.IsStdPkg(pkg) |
| } |
| |
| func (w *PkgWalker) importPath(path string, mode build.ImportMode) (*build.Package, error) { |
| if filepath.IsAbs(path) { |
| return w.context.ImportDir(path, 0) |
| } |
| if stdlib.IsStdPkg(path) { |
| return stdlib.ImportStdPkg(w.context, path, mode) |
| } |
| return w.context.Import(path, "", mode) |
| } |
| |
| func (w *PkgWalker) Import(parentDir string, name string, conf *PkgConfig) (pkg *types.Package, err error) { |
| defer func() { |
| err := recover() |
| if err != nil && typesVerbose { |
| log.Println(err) |
| } |
| }() |
| |
| if strings.HasPrefix(name, ".") && parentDir != "" { |
| name = filepath.Join(parentDir, name) |
| } |
| pkg = w.imported[name] |
| if pkg != nil { |
| if pkg == &w.importing { |
| return nil, fmt.Errorf("cycle importing package %q", name) |
| } |
| return pkg, nil |
| } |
| |
| if typesVerbose { |
| log.Println("parser pkg", name) |
| } |
| |
| bp, err := w.importPath(name, 0) |
| if err != nil { |
| return nil, err |
| } |
| |
| checkName := name |
| |
| if bp.ImportPath == "." { |
| checkName = bp.Name |
| } else { |
| checkName = bp.ImportPath |
| } |
| |
| if err != nil { |
| return nil, err |
| //if _, nogo := err.(*build.NoGoError); nogo { |
| // return |
| //} |
| //return |
| //log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, info.Dir, err) |
| } |
| |
| filenames := append(append([]string{}, bp.GoFiles...), bp.CgoFiles...) |
| if conf.WithTestFiles { |
| filenames = append(filenames, bp.TestGoFiles...) |
| } |
| |
| if name == "runtime" { |
| n := fmt.Sprintf("zgoos_%s.go", w.context.GOOS) |
| if !contains(filenames, n) { |
| filenames = append(filenames, n) |
| } |
| |
| n = fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH) |
| if !contains(filenames, n) { |
| filenames = append(filenames, n) |
| } |
| } |
| |
| parserFiles := func(filenames []string, cursor *FileCursor, xtest bool) (files []*ast.File) { |
| for _, file := range filenames { |
| var f *ast.File |
| if cursor != nil && cursor.fileName == file { |
| f, err = w.parseFile(bp.Dir, file, cursor.src) |
| cursor.pos = token.Pos(w.fset.File(f.Pos()).Base()) + token.Pos(cursor.cursorPos) |
| cursor.fileDir = bp.Dir |
| cursor.xtest = xtest |
| } else { |
| f, err = w.parseFile(bp.Dir, file, nil) |
| } |
| if err != nil && typesVerbose { |
| log.Printf("error parsing package %s: %s\n", name, err) |
| } |
| files = append(files, f) |
| } |
| return |
| } |
| files := parserFiles(filenames, conf.Cursor, false) |
| xfiles := parserFiles(bp.XTestGoFiles, conf.Cursor, true) |
| |
| typesConf := types.Config{ |
| IgnoreFuncBodies: conf.IgnoreFuncBodies, |
| FakeImportC: true, |
| Packages: w.gcimporter, |
| Import: func(imports map[string]*types.Package, name string) (pkg *types.Package, err error) { |
| if pkg != nil { |
| return pkg, nil |
| } |
| if conf.AllowBinary && w.isBinaryPkg(name) { |
| pkg = w.gcimporter[name] |
| if pkg != nil && pkg.Complete() { |
| return |
| } |
| pkg, err = gcimporter.Import(imports, name) |
| if pkg != nil && pkg.Complete() { |
| w.gcimporter[name] = pkg |
| return |
| } |
| } |
| return w.Import(bp.Dir, name, &PkgConfig{IgnoreFuncBodies: true, AllowBinary: true, WithTestFiles: false}) |
| }, |
| Error: func(err error) { |
| if typesVerbose { |
| log.Println(err) |
| } |
| }, |
| } |
| if pkg == nil { |
| pkg, err = typesConf.Check(checkName, w.fset, files, conf.Info) |
| conf.Pkg = pkg |
| } |
| w.imported[name] = pkg |
| |
| if len(xfiles) > 0 { |
| xpkg, _ := typesConf.Check(checkName+"_test", w.fset, xfiles, conf.XInfo) |
| w.imported[checkName+"_test"] = xpkg |
| conf.XPkg = xpkg |
| } |
| return |
| } |
| |
| func (w *PkgWalker) parseFile(dir, file string, src interface{}) (*ast.File, error) { |
| filename := filepath.Join(dir, file) |
| f, _ := w.parsedFileCache[filename] |
| if f != nil { |
| return f, nil |
| } |
| |
| var err error |
| |
| // generate missing context-dependent files. |
| if w.context != nil && file == fmt.Sprintf("zgoos_%s.go", w.context.GOOS) { |
| src := fmt.Sprintf("package runtime; const theGoos = `%s`", w.context.GOOS) |
| f, err = parser.ParseFile(w.fset, filename, src, 0) |
| if err != nil { |
| log.Fatalf("incorrect generated file: %s", err) |
| } |
| } |
| |
| if w.context != nil && file == fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH) { |
| src := fmt.Sprintf("package runtime; const theGoarch = `%s`", w.context.GOARCH) |
| f, err = parser.ParseFile(w.fset, filename, src, 0) |
| if err != nil { |
| log.Fatalf("incorrect generated file: %s", err) |
| } |
| } |
| |
| if f == nil { |
| flag := parser.AllErrors |
| if typesFindDoc { |
| flag |= parser.ParseComments |
| } |
| f, err = parser.ParseFile(w.fset, filename, src, flag) |
| if err != nil { |
| return f, err |
| } |
| } |
| |
| w.parsedFileCache[filename] = f |
| return f, nil |
| } |
| |
| func (w *PkgWalker) LookupCursor(pkg *types.Package, conf *PkgConfig, cursor *FileCursor) { |
| is := w.CheckIsImport(cursor) |
| if is != nil { |
| if cursor.xtest { |
| w.LookupImport(conf.XPkg, conf.XInfo, cursor, is) |
| } else { |
| w.LookupImport(conf.Pkg, conf.Info, cursor, is) |
| } |
| } else { |
| w.LookupObjects(conf, cursor) |
| } |
| } |
| |
| func (w *PkgWalker) LookupImport(pkg *types.Package, pkgInfo *types.Info, cursor *FileCursor, is *ast.ImportSpec) { |
| fpath, err := strconv.Unquote(is.Path.Value) |
| if err != nil { |
| return |
| } |
| |
| if typesFindDef { |
| fmt.Println(w.fset.Position(is.Pos())) |
| } |
| |
| fbase := fpath |
| pos := strings.LastIndexAny(fpath, "./-\\") |
| if pos != -1 { |
| fbase = fpath[pos+1:] |
| } |
| |
| var fname string |
| if is.Name != nil { |
| fname = is.Name.Name |
| } else { |
| fname = fbase |
| } |
| |
| if typesFindInfo { |
| if fname == fpath { |
| fmt.Printf("package %s\n", fname) |
| } else { |
| fmt.Printf("package %s (\"%s\")\n", fname, fpath) |
| } |
| } |
| |
| if !typesFindUse { |
| return |
| } |
| |
| fid := pkg.Path() + "." + fname |
| var usages []int |
| for id, obj := range pkgInfo.Uses { |
| if obj != nil && obj.Id() == fid { //!= nil && cursorObj.Pos() == obj.Pos() { |
| usages = append(usages, int(id.Pos())) |
| } |
| } |
| (sort.IntSlice(usages)).Sort() |
| for _, pos := range usages { |
| fmt.Println(w.fset.Position(token.Pos(pos))) |
| } |
| } |
| |
| func parserObjKind(obj types.Object) (ObjKind, error) { |
| var kind ObjKind |
| switch t := obj.(type) { |
| case *types.PkgName: |
| kind = ObjPkgName |
| case *types.Const: |
| kind = ObjConst |
| case *types.TypeName: |
| kind = ObjTypeName |
| switch t.Type().Underlying().(type) { |
| case *types.Interface: |
| kind = ObjInterface |
| case *types.Struct: |
| kind = ObjStruct |
| } |
| case *types.Var: |
| kind = ObjVar |
| if t.IsField() { |
| kind = ObjField |
| } |
| case *types.Func: |
| kind = ObjFunc |
| if sig, ok := t.Type().(*types.Signature); ok { |
| if sig.Recv() != nil { |
| kind = ObjMethod |
| } |
| } |
| case *types.Label: |
| kind = ObjLabel |
| case *types.Builtin: |
| kind = ObjBuiltin |
| case *types.Nil: |
| kind = ObjNil |
| default: |
| return ObjNone, fmt.Errorf("unknown obj type %T", obj) |
| } |
| return kind, nil |
| } |
| |
| func (w *PkgWalker) LookupStructFromField(info *types.Info, cursorPkg *types.Package, cursorObj types.Object, cursorPos token.Pos) types.Object { |
| if info == nil { |
| conf := &PkgConfig{ |
| IgnoreFuncBodies: true, |
| AllowBinary: true, |
| WithTestFiles: true, |
| Info: &types.Info{ |
| Defs: make(map[*ast.Ident]types.Object), |
| }, |
| } |
| w.imported[cursorPkg.Path()] = nil |
| pkg, _ := w.Import("", cursorPkg.Path(), conf) |
| if pkg != nil { |
| info = conf.Info |
| } |
| } |
| for _, obj := range info.Defs { |
| if obj == nil { |
| continue |
| } |
| if _, ok := obj.(*types.TypeName); ok { |
| if t, ok := obj.Type().Underlying().(*types.Struct); ok { |
| for i := 0; i < t.NumFields(); i++ { |
| if t.Field(i).Pos() == cursorPos { |
| return obj |
| } |
| } |
| } |
| } |
| } |
| return nil |
| } |
| |
| func (w *PkgWalker) lookupNamedField(named *types.Named, name string) *types.Named { |
| if istruct, ok := named.Underlying().(*types.Struct); ok { |
| for i := 0; i < istruct.NumFields(); i++ { |
| field := istruct.Field(i) |
| if field.Anonymous() { |
| fieldType := orgType(field.Type()) |
| if typ, ok := fieldType.(*types.Named); ok { |
| if na := w.lookupNamedField(typ, name); na != nil { |
| return na |
| } |
| } |
| } else { |
| if field.Name() == name { |
| return named |
| } |
| } |
| } |
| } |
| return nil |
| } |
| |
| func (w *PkgWalker) lookupNamedMethod(named *types.Named, name string) (types.Object, *types.Named) { |
| if iface, ok := named.Underlying().(*types.Interface); ok { |
| for i := 0; i < iface.NumMethods(); i++ { |
| fn := iface.Method(i) |
| if fn.Name() == name { |
| return fn, named |
| } |
| } |
| for i := 0; i < iface.NumEmbeddeds(); i++ { |
| if obj, na := w.lookupNamedMethod(iface.Embedded(i), name); obj != nil { |
| return obj, na |
| } |
| } |
| return nil, nil |
| } |
| if istruct, ok := named.Underlying().(*types.Struct); ok { |
| for i := 0; i < named.NumMethods(); i++ { |
| fn := named.Method(i) |
| if fn.Name() == name { |
| return fn, named |
| } |
| } |
| for i := 0; i < istruct.NumFields(); i++ { |
| field := istruct.Field(i) |
| if !field.Anonymous() { |
| continue |
| } |
| if typ, ok := field.Type().(*types.Named); ok { |
| if obj, na := w.lookupNamedMethod(typ, name); obj != nil { |
| return obj, na |
| } |
| } |
| } |
| } |
| return nil, nil |
| } |
| |
| func IsSamePkg(a, b *types.Package) bool { |
| if a == b { |
| return true |
| } |
| if a == nil || b == nil { |
| return false |
| } |
| return a.Path() == b.Path() |
| } |
| |
| func IsSameObject(a, b types.Object) bool { |
| if a == b { |
| return true |
| } |
| if a == nil || b == nil { |
| return false |
| } |
| var apath string |
| var bpath string |
| if a.Pkg() != nil { |
| apath = a.Pkg().Path() |
| } |
| if b.Pkg() != nil { |
| bpath = b.Pkg().Path() |
| } |
| if apath != bpath { |
| return false |
| } |
| if a.Id() != b.Id() { |
| return false |
| } |
| return a.String() == b.String() |
| } |
| |
| func orgType(typ types.Type) types.Type { |
| if pt, ok := typ.(*types.Pointer); ok { |
| return pt.Elem() |
| } |
| return typ |
| } |
| |
| func (w *PkgWalker) LookupObjects(conf *PkgConfig, cursor *FileCursor) { |
| var cursorObj types.Object |
| var cursorSelection *types.Selection |
| var cursorObjIsDef bool |
| //lookup defs |
| |
| var pkg *types.Package |
| var pkgInfo *types.Info |
| if cursor.xtest { |
| pkgInfo = conf.XInfo |
| pkg = conf.XPkg |
| } else { |
| pkgInfo = conf.Info |
| pkg = conf.Pkg |
| } |
| |
| _ = cursorObjIsDef |
| if cursorObj == nil { |
| for sel, obj := range pkgInfo.Selections { |
| if cursor.pos >= sel.Sel.Pos() && cursor.pos <= sel.Sel.End() { |
| cursorObj = obj.Obj() |
| cursorSelection = obj |
| break |
| } |
| } |
| } |
| if cursorObj == nil { |
| for id, obj := range pkgInfo.Defs { |
| if cursor.pos >= id.Pos() && cursor.pos <= id.End() { |
| cursorObj = obj |
| cursorObjIsDef = true |
| break |
| } |
| } |
| } |
| _ = cursorSelection |
| if cursorObj == nil { |
| for id, obj := range pkgInfo.Uses { |
| if cursor.pos >= id.Pos() && cursor.pos <= id.End() { |
| cursorObj = obj |
| break |
| } |
| } |
| } |
| if cursorObj == nil { |
| return |
| } |
| kind, err := parserObjKind(cursorObj) |
| if err != nil { |
| log.Fatalln(err) |
| } |
| |
| if kind == ObjField { |
| if cursorObj.(*types.Var).Anonymous() { |
| typ := orgType(cursorObj.Type()) |
| if named, ok := typ.(*types.Named); ok { |
| cursorObj = named.Obj() |
| } |
| } |
| } |
| cursorPkg := cursorObj.Pkg() |
| cursorPos := cursorObj.Pos() |
| //var fieldTypeInfo *types.Info |
| var fieldTypeObj types.Object |
| // if cursorPkg == pkg { |
| // fieldTypeInfo = pkgInfo |
| // } |
| cursorIsInterfaceMethod := false |
| var cursorInterfaceTypeName string |
| if kind == ObjMethod && cursorSelection != nil && cursorSelection.Recv() != nil { |
| sig := cursorObj.(*types.Func).Type().Underlying().(*types.Signature) |
| if _, ok := sig.Recv().Type().Underlying().(*types.Interface); ok { |
| if named, ok := cursorSelection.Recv().(*types.Named); ok { |
| obj, typ := w.lookupNamedMethod(named, cursorObj.Name()) |
| if obj != nil { |
| cursorObj = obj |
| } |
| if typ != nil { |
| cursorPkg = typ.Obj().Pkg() |
| cursorInterfaceTypeName = typ.Obj().Name() |
| } |
| cursorIsInterfaceMethod = true |
| } |
| } |
| } else if kind == ObjField && cursorSelection != nil { |
| if recv := cursorSelection.Recv(); recv != nil { |
| typ := orgType(recv) |
| if typ != nil { |
| if name, ok := typ.(*types.Named); ok { |
| fieldTypeObj = name.Obj() |
| na := w.lookupNamedField(name, cursorObj.Name()) |
| if na != nil { |
| fieldTypeObj = na.Obj() |
| } |
| } |
| } |
| } |
| } |
| if cursorPkg != nil && cursorPkg != pkg && |
| kind != ObjPkgName && w.isBinaryPkg(cursorPkg.Path()) { |
| conf := &PkgConfig{ |
| IgnoreFuncBodies: true, |
| AllowBinary: true, |
| WithTestFiles: true, |
| Info: &types.Info{ |
| Defs: make(map[*ast.Ident]types.Object), |
| }, |
| } |
| pkg, _ := w.Import("", cursorPkg.Path(), conf) |
| if pkg != nil { |
| if cursorIsInterfaceMethod { |
| for _, obj := range conf.Info.Defs { |
| if obj == nil { |
| continue |
| } |
| if fn, ok := obj.(*types.Func); ok { |
| if fn.Name() == cursorObj.Name() { |
| if sig, ok := fn.Type().Underlying().(*types.Signature); ok { |
| if named, ok := sig.Recv().Type().(*types.Named); ok { |
| if named.Obj() != nil && named.Obj().Name() == cursorInterfaceTypeName { |
| cursorPos = obj.Pos() |
| break |
| } |
| } |
| } |
| } |
| } |
| } |
| } else if kind == ObjField && fieldTypeObj != nil { |
| for _, obj := range conf.Info.Defs { |
| if obj == nil { |
| continue |
| } |
| if _, ok := obj.(*types.TypeName); ok { |
| if IsSameObject(fieldTypeObj, obj) { |
| if t, ok := obj.Type().Underlying().(*types.Struct); ok { |
| for i := 0; i < t.NumFields(); i++ { |
| if t.Field(i).Id() == cursorObj.Id() { |
| cursorPos = t.Field(i).Pos() |
| break |
| } |
| } |
| } |
| break |
| } |
| } |
| } |
| } else { |
| for k, v := range conf.Info.Defs { |
| if k != nil && v != nil && IsSameObject(v, cursorObj) { |
| cursorPos = k.Pos() |
| break |
| } |
| } |
| } |
| } |
| // if kind == ObjField || cursorIsInterfaceMethod { |
| // fieldTypeInfo = conf.Info |
| // } |
| } |
| // if kind == ObjField { |
| // fieldTypeObj = w.LookupStructFromField(fieldTypeInfo, cursorPkg, cursorObj, cursorPos) |
| // } |
| if typesFindDef { |
| fmt.Println(w.fset.Position(cursorPos)) |
| } |
| if typesFindInfo { |
| if kind == ObjField && fieldTypeObj != nil { |
| typeName := fieldTypeObj.Name() |
| if fieldTypeObj.Pkg() != nil && fieldTypeObj.Pkg() != pkg { |
| typeName = fieldTypeObj.Pkg().Name() + "." + fieldTypeObj.Name() |
| } |
| fmt.Println(typeName, simpleObjInfo(cursorObj)) |
| } else if kind == ObjBuiltin { |
| fmt.Println(builtinInfo(cursorObj.Name())) |
| } else if kind == ObjPkgName { |
| fmt.Println(cursorObj.String()) |
| } else if cursorIsInterfaceMethod { |
| fmt.Println(strings.Replace(simpleObjInfo(cursorObj), "(interface)", cursorPkg.Name()+"."+cursorInterfaceTypeName, 1)) |
| } else { |
| fmt.Println(simpleObjInfo(cursorObj)) |
| } |
| } |
| |
| if typesFindDoc && typesFindDef { |
| pos := w.fset.Position(cursorPos) |
| file := w.parsedFileCache[pos.Filename] |
| if file != nil { |
| line := pos.Line |
| var group *ast.CommentGroup |
| for _, v := range file.Comments { |
| lastLine := w.fset.Position(v.End()).Line |
| if lastLine == line || lastLine == line-1 { |
| group = v |
| } else if lastLine > line { |
| break |
| } |
| } |
| if group != nil { |
| fmt.Println(group.Text()) |
| } |
| } |
| } |
| if !typesFindUse { |
| return |
| } |
| |
| var usages []int |
| if kind == ObjPkgName { |
| for id, obj := range pkgInfo.Uses { |
| if obj != nil && obj.Id() == cursorObj.Id() { //!= nil && cursorObj.Pos() == obj.Pos() { |
| usages = append(usages, int(id.Pos())) |
| } |
| } |
| } else { |
| // for id, obj := range pkgInfo.Defs { |
| // if obj == cursorObj { //!= nil && cursorObj.Pos() == obj.Pos() { |
| // usages = append(usages, int(id.Pos())) |
| // } |
| // } |
| for id, obj := range pkgInfo.Uses { |
| if obj == cursorObj { //!= nil && cursorObj.Pos() == obj.Pos() { |
| usages = append(usages, int(id.Pos())) |
| } |
| } |
| } |
| var pkg_path string |
| var xpkg_path string |
| if conf.Pkg != nil { |
| pkg_path = conf.Pkg.Path() |
| } |
| if conf.XPkg != nil { |
| xpkg_path = conf.XPkg.Path() |
| } |
| |
| if cursorPkg != nil && (cursorPkg.Path() == pkg_path || |
| cursorPkg.Path() == xpkg_path) { |
| usages = append(usages, int(cursorPos)) |
| } |
| |
| (sort.IntSlice(usages)).Sort() |
| for _, pos := range usages { |
| fmt.Println(w.fset.Position(token.Pos(pos))) |
| } |
| //check look for current pkg.object on pkg_test |
| if typesFindUseAll || IsSamePkg(cursorPkg, conf.Pkg) { |
| var addInfo *types.Info |
| if conf.Cursor.xtest { |
| addInfo = conf.Info |
| } else { |
| addInfo = conf.XInfo |
| } |
| if addInfo != nil && cursorPkg != nil { |
| var usages []int |
| // for id, obj := range addInfo.Defs { |
| // if id != nil && obj != nil && obj.Id() == cursorObj.Id() { |
| // usages = append(usages, int(id.Pos())) |
| // } |
| // } |
| for k, v := range addInfo.Uses { |
| if k != nil && v != nil && IsSameObject(v, cursorObj) { |
| usages = append(usages, int(k.Pos())) |
| } |
| } |
| (sort.IntSlice(usages)).Sort() |
| for _, pos := range usages { |
| fmt.Println(w.fset.Position(token.Pos(pos))) |
| } |
| } |
| } |
| if !typesFindUseAll { |
| return |
| } |
| |
| if cursorPkg == nil { |
| return |
| } |
| |
| var find_def_pkg string |
| var uses_paths []string |
| if cursorPkg.Path() != pkg_path && cursorPkg.Path() != xpkg_path { |
| find_def_pkg = cursorPkg.Path() |
| uses_paths = append(uses_paths, cursorPkg.Path()) |
| } |
| |
| buildutil.ForEachPackage(&build.Default, func(importPath string, err error) { |
| if err != nil { |
| return |
| } |
| if importPath == conf.Pkg.Path() { |
| return |
| } |
| bp, err := w.importPath(importPath, 0) |
| if err != nil { |
| return |
| } |
| find := false |
| if bp.ImportPath == cursorPkg.Path() { |
| find = true |
| } else { |
| for _, v := range bp.Imports { |
| if v == cursorObj.Pkg().Path() { |
| find = true |
| break |
| } |
| } |
| } |
| if find { |
| for _, v := range uses_paths { |
| if v == bp.ImportPath { |
| return |
| } |
| } |
| uses_paths = append(uses_paths, bp.ImportPath) |
| } |
| }) |
| |
| w.imported = make(map[string]*types.Package) |
| for _, v := range uses_paths { |
| conf := &PkgConfig{ |
| IgnoreFuncBodies: false, |
| AllowBinary: true, |
| WithTestFiles: true, |
| Info: &types.Info{ |
| Uses: make(map[*ast.Ident]types.Object), |
| }, |
| XInfo: &types.Info{ |
| Uses: make(map[*ast.Ident]types.Object), |
| }, |
| } |
| w.imported[v] = nil |
| var usages []int |
| vpkg, _ := w.Import("", v, conf) |
| if vpkg != nil && vpkg != pkg { |
| if conf.Info != nil { |
| for k, v := range conf.Info.Uses { |
| if k != nil && v != nil && IsSameObject(v, cursorObj) { |
| usages = append(usages, int(k.Pos())) |
| } |
| } |
| } |
| if conf.XInfo != nil { |
| for k, v := range conf.XInfo.Uses { |
| if k != nil && v != nil && IsSameObject(v, cursorObj) { |
| usages = append(usages, int(k.Pos())) |
| } |
| } |
| } |
| } |
| if v == find_def_pkg { |
| usages = append(usages, int(cursorPos)) |
| } |
| (sort.IntSlice(usages)).Sort() |
| for _, pos := range usages { |
| fmt.Println(w.fset.Position(token.Pos(pos))) |
| } |
| } |
| } |
| |
| func (w *PkgWalker) CheckIsImport(cursor *FileCursor) *ast.ImportSpec { |
| if cursor.fileDir == "" { |
| return nil |
| } |
| file, _ := w.parseFile(cursor.fileDir, cursor.fileName, cursor.src) |
| if file == nil { |
| return nil |
| } |
| for _, is := range file.Imports { |
| if inRange(is, cursor.pos) { |
| return is |
| } |
| } |
| return nil |
| } |
| |
| func inRange(node ast.Node, p token.Pos) bool { |
| if node == nil { |
| return false |
| } |
| return p >= node.Pos() && p <= node.End() |
| } |
| |
| func (w *PkgWalker) nodeString(node interface{}) string { |
| if node == nil { |
| return "" |
| } |
| var b bytes.Buffer |
| printer.Fprint(&b, w.fset, node) |
| return b.String() |
| } |