// checkInPackageBlock performs safety checks for renames of // func/var/const/type objects in the package block. func (e *Export) checkInPackageBlock(from types.Object, to string) { info := e.u.pkgInfo lexinfo := lexical.Structure(e.u.prog.Fset, from.Pkg(), &info.Info, info.Files) // We don't rename anything in the package block to init, as that might // conflict or otherwise break stuff if to == "init" { e.Conflicting = true return } // Check for conflicts between package block and all file blocks. for _, f := range info.Files { if _, b := lexinfo.Blocks[f].Lookup(to); b == lexinfo.Blocks[f] { e.Conflicting = true return } } if f, ok := from.(*types.Func); ok && recv(f) == nil { e.checkFunction(f, to) if e.Conflicting { return } } // Check for conflicts in lexical scope. // Do not need to check all imported packages: // Since it's unnecessarily exported, no one else is going to be sad if I unexport it! e.checkInLexicalScope(from, to) }
func (r *Unexporter) checkInLocalScope(objsToUpdate map[types.Object]string, from types.Object, to string) { info := r.packages[from.Pkg()] // Is this object an implicit local var for a type switch? // Each case has its own var, whose position is the decl of y, // but Ident in that decl does not appear in the Uses map. // // switch y := x.(type) { // Defs[Ident(y)] is undefined // case int: print(y) // Implicits[CaseClause(int)] = Var(y_int) // case string: print(y) // Implicits[CaseClause(string)] = Var(y_string) // } // var isCaseVar bool for syntax, obj := range info.Implicits { if _, ok := syntax.(*ast.CaseClause); ok && obj.Pos() == from.Pos() { isCaseVar = true r.check(objsToUpdate, obj, to) } } r.checkInLexicalScope(objsToUpdate, from, to, info) // Finally, if this was a type switch, change the variable y. if isCaseVar { _, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos()) path[0].(*ast.Ident).Name = to // path is [Ident AssignStmt TypeSwitchStmt...] } }
func formatMember(obj types.Object, maxname int) string { qualifier := types.RelativeTo(obj.Pkg()) var buf bytes.Buffer fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name()) switch obj := obj.(type) { case *types.Const: fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), obj.Val().String()) case *types.Func: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) case *types.TypeName: // Abbreviate long aggregate type names. var abbrev string switch t := obj.Type().Underlying().(type) { case *types.Interface: if t.NumMethods() > 1 { abbrev = "interface{...}" } case *types.Struct: if t.NumFields() > 1 { abbrev = "struct{...}" } } if abbrev == "" { fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier)) } else { fmt.Fprintf(&buf, " %s", abbrev) } case *types.Var: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) } return buf.String() }
// checkSelection checks that all uses and selections that resolve to // the specified object would continue to do so after the renaming. func (r *Unexporter) checkSelections(objsToUpdate map[types.Object]string, from types.Object, to string) { for pkg, info := range r.packages { if id := someUse(info, from); id != nil { if !r.checkExport(id, pkg, from, to) { return } } for syntax, sel := range info.Selections { // There may be extant selections of only the old // name or only the new name, so we must check both. // (If neither, the renaming is sound.) // // In both cases, we wish to compare the lengths // of the implicit field path (Selection.Index) // to see if the renaming would change it. // // If a selection that resolves to 'from', when renamed, // would yield a path of the same or shorter length, // this indicates ambiguity or a changed referent, // analogous to same- or sub-block lexical conflict. // // If a selection using the name 'to' would // yield a path of the same or shorter length, // this indicates ambiguity or shadowing, // analogous to same- or super-block lexical conflict. // TODO(adonovan): fix: derive from Types[syntax.X].Mode // TODO(adonovan): test with pointer, value, addressable value. isAddressable := true if sel.Obj() == from { if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), to); obj != nil { // Renaming this existing selection of // 'from' may block access to an existing // type member named 'to'. delta := len(indices) - len(sel.Index()) if delta > 0 { continue // no ambiguity } r.selectionConflict(objsToUpdate, from, to, delta, syntax, obj) return } } else if sel.Obj().Name() == to { if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from { // Renaming 'from' may cause this existing // selection of the name 'to' to change // its meaning. delta := len(indices) - len(sel.Index()) if delta > 0 { continue // no ambiguity } r.selectionConflict(objsToUpdate, from, to, -delta, syntax, sel.Obj()) return } } } } }
// checkInPackageBlock performs safety checks for renames of // func/var/const/type objects in the package block. func (r *renamer) checkInPackageBlock(from types.Object) { // Check that there are no references to the name from another // package if the renaming would make it unexported. if ast.IsExported(from.Name()) && !ast.IsExported(r.to) { for pkg, info := range r.packages { if pkg == from.Pkg() { continue } if id := someUse(info, from); id != nil && !r.checkExport(id, pkg, from) { break } } } info := r.packages[from.Pkg()] // Check that in the package block, "init" is a function, and never referenced. if r.to == "init" { kind := objectKind(from) if kind == "func" { // Reject if intra-package references to it exist. for id, obj := range info.Uses { if obj == from { r.errorf(from.Pos(), "renaming this func %q to %q would make it a package initializer", from.Name(), r.to) r.errorf(id.Pos(), "\tbut references to it exist") break } } } else { r.errorf(from.Pos(), "you cannot have a %s at package level named %q", kind, r.to) } } // Check for conflicts between package block and all file blocks. for _, f := range info.Files { fileScope := info.Info.Scopes[f] b, prev := fileScope.LookupParent(r.to, token.NoPos) if b == fileScope { r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", objectKind(from), from.Name(), r.to) r.errorf(prev.Pos(), "\twith this %s", objectKind(prev)) return // since checkInPackageBlock would report redundant errors } } // Check for conflicts in lexical scope. if from.Exported() { for _, info := range r.packages { r.checkInLexicalScope(from, info) } } else { r.checkInLexicalScope(from, info) } }
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 }
// checkInPackageBlock performs safety checks for renames of // func/var/const/type objects in the package block. func (r *Unexporter) checkInPackageBlock(objsToUpdate map[types.Object]string, from types.Object, to string) { // Check that there are no references to the name from another // package if the renaming would make it unexported. if ast.IsExported(from.Name()) && !ast.IsExported(to) { for pkg, info := range r.packages { if pkg == from.Pkg() { continue } if id := someUse(info, from); id != nil && !r.checkExport(id, pkg, from, to) { break } } } info := r.packages[from.Pkg()] lexinfo := r.lexInfo(info) // Check that in the package block, "init" is a function, and never referenced. if to == "init" { kind := objectKind(from) if kind == "func" { // Reject if intra-package references to it exist. if refs := lexinfo.Refs[from]; len(refs) > 0 { r.warn(from, r.errorf(from.Pos(), "renaming this func %q to %q would make it a package initializer", from.Name(), to), r.errorf(refs[0].Id.Pos(), "\tbut references to it exist")) } } else { r.warn(from, r.errorf(from.Pos(), "you cannot have a %s at package level named %q", kind, to)) } } // Check for conflicts between package block and all file blocks. for _, f := range info.Files { if prev, b := lexinfo.Blocks[f].Lookup(to); b == lexinfo.Blocks[f] { r.warn(from, r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", objectKind(from), from.Name(), to), r.errorf(prev.Pos(), "\twith this %s", objectKind(prev))) return // since checkInPackageBlock would report redundant errors } } // Check for conflicts in lexical scope. if from.Exported() { for _, info := range r.packages { r.checkInLexicalScope(objsToUpdate, from, to, info) } } else { r.checkInLexicalScope(objsToUpdate, from, to, info) } }
func (sym *symtab) addSymbol(obj types.Object) { fn := types.ObjectString(obj, nil) n := obj.Name() pkg := obj.Pkg() id := n if pkg != nil { id = pkg.Name() + "_" + n } switch obj.(type) { case *types.Const: sym.syms[fn] = &symbol{ gopkg: pkg, goobj: obj, kind: skConst, id: id, goname: n, cgoname: "cgo_const_" + id, cpyname: "cpy_const_" + id, } sym.addType(obj, obj.Type()) case *types.Var: sym.syms[fn] = &symbol{ gopkg: pkg, goobj: obj, kind: skVar, id: id, goname: n, cgoname: "cgo_var_" + id, cpyname: "cpy_var_" + id, } sym.addType(obj, obj.Type()) case *types.Func: sym.syms[fn] = &symbol{ gopkg: pkg, goobj: obj, kind: skFunc, id: id, goname: n, cgoname: "cgo_func_" + id, cpyname: "cpy_func_" + id, } sig := obj.Type().Underlying().(*types.Signature) sym.processTuple(sig.Params()) sym.processTuple(sig.Results()) case *types.TypeName: sym.addType(obj, obj.Type()) default: panic(fmt.Errorf("gopy: handled object [%#v]", obj)) } }
func newObject(obj types.Object, sel *types.Selection) (*Object, error) { // WARN: Dev only if sel != nil { return newSelector(sel) } o := &Object{ Name: obj.Name(), pos: obj.Pos(), } o.setPkg(obj.Pkg()) switch typ := obj.(type) { case *types.PkgName: o.ObjType = Package if p := typ.Imported(); p != nil { o.setPkg(p) } case *types.Const: o.ObjType = Const case *types.TypeName: o.ObjType = TypeName case *types.Var: o.ObjType = Var o.IsField = typ.IsField() if t, ok := derefType(typ.Type()).(*types.Named); ok { o.ObjType = TypeName // WARN: This looks wrong o.IsField = false if obj := t.Obj(); obj != nil { o.Name = obj.Name() o.setPkg(obj.Pkg()) o.pos = obj.Pos() // WARN } } case *types.Func: if sig := typ.Type().(*types.Signature); sig != nil { o.ObjType = Func } else { switch r := derefType(sig.Recv().Type()).(type) { case *types.Named: o.ObjType = Method o.setParent(r.Obj()) case *types.Interface: o.ObjType = Interface default: // This should never happen } } default: // TODO: log type o.ObjType = Bad } return o, nil }
func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool { // Reject cross-package references if r.to is unexported. // (Such references may be qualified identifiers or field/method // selections.) if !ast.IsExported(r.to) && pkg != from.Pkg() { r.errorf(from.Pos(), "renaming this %s %q to %q would make it unexported", objectKind(from), from.Name(), r.to) r.errorf(id.Pos(), "\tbreaking references from packages such as %q", pkg.Path()) return false } return true }
func getKey(obj types.Object) object { if obj == nil { return object{} } pkg := obj.Pkg() pkgPath := "" if pkg != nil { pkgPath = pkg.Path() } return object{ pkgPath: pkgPath, name: obj.Name(), } }
func newFuncFrom(p *Package, parent string, obj types.Object, sig *types.Signature) (Func, error) { haserr := false res := sig.Results() var ret types.Type switch res.Len() { case 2: if !isErrorType(res.At(1).Type()) { return Func{}, fmt.Errorf( "bind: second result value must be of type error: %s", obj, ) } haserr = true ret = res.At(0).Type() case 1: if isErrorType(res.At(0).Type()) { haserr = true ret = nil } else { ret = res.At(0).Type() } case 0: ret = nil default: return Func{}, fmt.Errorf("bind: too many results to return: %v", obj) } id := obj.Pkg().Name() + "_" + obj.Name() if parent != "" { id = obj.Pkg().Name() + "_" + parent + "_" + obj.Name() } return Func{ pkg: p, sig: newSignatureFrom(p, sig), typ: obj.Type(), name: obj.Name(), id: id, doc: p.getDoc(parent, obj), ret: ret, err: haserr, }, nil }
func (g *Grapher) makeDefInfo(obj types.Object) (*DefKey, *defInfo, error) { switch obj := obj.(type) { case *types.Builtin: return &DefKey{"builtin", []string{obj.Name()}}, &defInfo{pkgscope: false, exported: true}, nil case *types.Nil: return &DefKey{"builtin", []string{"nil"}}, &defInfo{pkgscope: false, exported: true}, nil case *types.TypeName: if basic, ok := obj.Type().(*types.Basic); ok { return &DefKey{"builtin", []string{basic.Name()}}, &defInfo{pkgscope: false, exported: true}, nil } if obj.Name() == "error" { return &DefKey{"builtin", []string{obj.Name()}}, &defInfo{pkgscope: false, exported: true}, nil } case *types.PkgName: return &DefKey{obj.Imported().Path(), []string{}}, &defInfo{pkgscope: false, exported: true}, nil case *types.Const: var pkg string if obj.Pkg() == nil { pkg = "builtin" } else { pkg = obj.Pkg().Path() } if obj.Val().Kind() == exact.Bool && pkg == "builtin" { return &DefKey{pkg, []string{obj.Name()}}, &defInfo{pkgscope: false, exported: true}, nil } } if obj.Pkg() == nil { // builtin return &DefKey{"builtin", []string{obj.Name()}}, &defInfo{pkgscope: false, exported: true}, nil } path := g.path(obj) // Handle the case where a dir has 2 main packages that are not // intended to be compiled together and have overlapping def // paths. Prefix the def path with the filename. if obj.Pkg().Name() == "main" { p := g.program.Fset.Position(obj.Pos()) path = append([]string{filepath.Base(p.Filename)}, path...) } return &DefKey{obj.Pkg().Path(), path}, &defInfo{pkgscope: g.pkgscope[obj], exported: g.exported[obj]}, nil }
func getEnclosingStruct(object types.Object) types.Type { pkgScope := object.Pkg().Scope() s := pkgScope.Innermost(object.Pos()) if s.Parent() == pkgScope { s = pkgScope } var obj types.Object for _, name := range s.Names() { o := s.Lookup(name) if o.Pos() <= object.Pos() && (obj == nil || o.Pos() > obj.Pos()) { obj = o } } if obj == nil { return nil } if obj == object { return obj.Type() } t := obj.Type().Underlying().(*types.Struct) for { var f types.Object for i := 0; i < t.NumFields(); i++ { field := t.Field(i) if field == object { return t } if field.Pos() <= object.Pos() && (f == nil || field.Pos() > f.Pos()) { f = field } } if fs, ok := f.Type().(*types.Struct); ok { t = fs } else { break } } return t }
func (c *funcContext) objectName(o types.Object) string { if isPkgLevel(o) { c.p.dependencies[o] = true if o.Pkg() != c.p.Pkg || (isVarOrConst(o) && o.Exported()) { return c.pkgVar(o.Pkg()) + "." + o.Name() } } name, ok := c.p.objectNames[o] if !ok { name = c.newVariableWithLevel(o.Name(), isPkgLevel(o)) c.p.objectNames[o] = name } if v, ok := o.(*types.Var); ok && c.p.escapingVars[v] { return name + "[0]" } return name }
func isExported(obj types.Object) bool { // https://golang.org/ref/spec#Exported_identifiers // An identifier is exported if both: // the first character of the identifier's name is a Unicode upper case letter (Unicode class "Lu"); and // the identifier is declared in the package block or it is a field name or method name. // All other identifiers are not exported. if !obj.Exported() { // does not start with an upper case letter return false } if v, ok := obj.(*types.Var); ok && v.IsField() { // is a field name return true } if sig, ok := obj.Type().(*types.Signature); ok && sig.Recv() != nil { // is a method name return true } // is declared in the package block return obj.Parent() == obj.Pkg().Scope() }
// checkSelection checks that all uses and selections that resolve to // the specified object would continue to do so after the renaming. func (e *Export) checkSelections(from types.Object, to string) { info := e.u.pkgInfo if id := someUse(info, from); id != nil { e.Conflicting = true return } for _, sel := range info.Selections { if sel.Obj() == from { if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), true, from.Pkg(), to); obj != nil { // Renaming this existing selection of // 'from' may block access to an existing // type member named 'to'. delta := len(indices) - len(sel.Index()) if delta > 0 { continue // no ambiguity } e.Conflicting = true return } } else if sel.Obj().Name() == to { if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), true, from.Pkg(), from.Name()); obj == from { // Renaming 'from' may cause this existing // selection of the name 'to' to change // its meaning. delta := len(indices) - len(sel.Index()) if delta > 0 { continue // no ambiguity } e.Conflicting = true return } } } }
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() }
// Referrers reports all identifiers that resolve to the same object // as the queried identifier, within any package in the analysis scope. func referrers(q *Query) error { lconf := loader.Config{Build: q.Build} allowErrors(&lconf) if _, err := importQueryPackage(q.Pos, &lconf); err != nil { return err } var id *ast.Ident var obj types.Object var lprog *loader.Program var pass2 bool var qpos *queryPos for { // Load/parse/type-check the program. var err error 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 } id, _ = qpos.path[0].(*ast.Ident) if id == nil { return fmt.Errorf("no identifier here") } obj = qpos.info.ObjectOf(id) if obj == nil { // Happens for y in "switch y := x.(type)", // the package declaration, // and unresolved identifiers. if _, ok := qpos.path[1].(*ast.File); ok { // package decl? pkg := qpos.info.Pkg obj = types.NewPkgName(id.Pos(), pkg, pkg.Name(), pkg) } else { return fmt.Errorf("no object for identifier: %T", qpos.path[1]) } } if pass2 { break } // If the identifier is exported, we must load all packages that // depend transitively upon the package that defines it. // Treat PkgNames as exported, even though they're lowercase. if _, isPkg := obj.(*types.PkgName); !(isPkg || obj.Exported()) { break // not exported } // Scan the workspace and build the import graph. // Ignore broken packages. _, rev, _ := importgraph.Build(q.Build) // Re-load the larger program. // Create a new file set so that ... // External test packages are never imported, // so they will never appear in the graph. // (We must reset the Config here, not just reset the Fset field.) lconf = loader.Config{ Fset: token.NewFileSet(), Build: q.Build, } allowErrors(&lconf) for path := range rev.Search(obj.Pkg().Path()) { lconf.ImportWithTests(path) } pass2 = true } // Iterate over all go/types' Uses facts for the entire program. var refs []*ast.Ident for _, info := range lprog.AllPackages { for id2, obj2 := range info.Uses { if sameObj(obj, obj2) { refs = append(refs, id2) } } } sort.Sort(byNamePos{q.Fset, refs}) q.result = &referrersResult{ qpos: qpos, query: id, obj: obj, refs: refs, } return nil }
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) && kind != ObjPkgName { 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 (c *funcContext) translateExpr(expr ast.Expr) *expression { exprType := c.p.TypeOf(expr) if value := c.p.Types[expr].Value; value != nil { basic := exprType.Underlying().(*types.Basic) switch { case isBoolean(basic): return c.formatExpr("%s", strconv.FormatBool(exact.BoolVal(value))) case isInteger(basic): if is64Bit(basic) { if basic.Kind() == types.Int64 { d, ok := exact.Int64Val(value) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatInt(d>>32, 10), strconv.FormatUint(uint64(d)&(1<<32-1), 10)) } d, ok := exact.Uint64Val(value) if !ok { panic("could not get exact uint") } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10)) } d, ok := exact.Int64Val(value) if !ok { panic("could not get exact int") } return c.formatExpr("%s", strconv.FormatInt(d, 10)) case isFloat(basic): f, _ := exact.Float64Val(value) return c.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64)) case isComplex(basic): r, _ := exact.Float64Val(exact.Real(value)) i, _ := exact.Float64Val(exact.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } return c.formatExpr("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case isString(basic): return c.formatExpr("%s", encodeString(exact.StringVal(value))) default: panic("Unhandled constant type: " + basic.String()) } } var obj types.Object switch e := expr.(type) { case *ast.SelectorExpr: obj = c.p.Uses[e.Sel] case *ast.Ident: obj = c.p.Defs[e] if obj == nil { obj = c.p.Uses[e] } } if obj != nil && typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Global": return c.formatExpr("$global") case "Module": return c.formatExpr("$module") case "Undefined": return c.formatExpr("undefined") } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { var elements []string i := 0 zero := c.translateExpr(c.zeroValue(elementType)).String() for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, ok := exact.Int64Val(c.p.Types[kve.Key].Value) if !ok { panic("could not get exact int") } i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateImplicitConversionWithCloning(element, elementType).String() i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) == 0 { return c.formatExpr("%s.zero()", c.typeName(t)) } zero := c.translateExpr(c.zeroValue(t.Elem())).String() for len(elements) < int(t.Len()) { elements = append(elements, zero) } return c.formatExpr(`$toNativeArray(%s, [%s])`, typeKind(t.Elem()), strings.Join(elements, ", ")) case *types.Slice: return c.formatExpr("new %s([%s])", c.typeName(exprType), strings.Join(collectIndexedElements(t.Elem()), ", ")) case *types.Map: entries := make([]string, len(e.Elts)) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) entries[i] = fmt.Sprintf("{ k: %s, v: %s }", c.translateImplicitConversionWithCloning(kve.Key, t.Key()), c.translateImplicitConversionWithCloning(kve.Value, t.Elem())) } return c.formatExpr("$makeMap(%s.keyFor, [%s])", c.typeName(t.Key()), strings.Join(entries, ", ")) case *types.Struct: elements := make([]string, t.NumFields()) isKeyValue := true if len(e.Elts) != 0 { _, isKeyValue = e.Elts[0].(*ast.KeyValueExpr) } if !isKeyValue { for i, element := range e.Elts { elements[i] = c.translateImplicitConversionWithCloning(element, t.Field(i).Type()).String() } } if isKeyValue { for i := range elements { elements[i] = c.translateExpr(c.zeroValue(t.Field(i).Type())).String() } for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) for j := range elements { if kve.Key.(*ast.Ident).Name == t.Field(j).Name() { elements[j] = c.translateImplicitConversionWithCloning(kve.Value, t.Field(j).Type()).String() break } } } } return c.formatExpr("new %s.ptr(%s)", c.typeName(exprType), strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: _, fun := translateFunction(e.Type, nil, e.Body, c, exprType.(*types.Signature), c.p.FuncLitInfos[e], "") if len(c.p.escapingVars) != 0 { names := make([]string, 0, len(c.p.escapingVars)) for obj := range c.p.escapingVars { names = append(names, c.p.objectNames[obj]) } sort.Strings(names) list := strings.Join(names, ", ") return c.formatExpr("(function(%s) { return %s; })(%s)", list, fun, list) } return c.formatExpr("(%s)", fun) case *ast.UnaryExpr: t := c.p.TypeOf(e.X) switch e.Op { case token.AND: if typesutil.IsJsObject(exprType) { return c.formatExpr("%e.object", e.X) } switch t.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } switch x := astutil.RemoveParens(e.X).(type) { case *ast.CompositeLit: return c.formatExpr("$newDataPointer(%e, %s)", x, c.typeName(c.p.TypeOf(e))) case *ast.Ident: obj := c.p.Uses[x].(*types.Var) if c.p.escapingVars[obj] { return c.formatExpr("(%1s.$ptr || (%1s.$ptr = new %2s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %1s)))", c.p.objectNames[obj], c.typeName(exprType)) } return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) case *ast.SelectorExpr: sel, ok := c.p.Selections[x] if !ok { // qualified identifier obj := c.p.Uses[x.Sel].(*types.Var) return c.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, c.varPtrName(obj), c.typeName(exprType), c.objectName(obj), c.translateAssign(x, c.newIdent("$v", exprType), false)) } newSel := &ast.SelectorExpr{X: c.newIdent("this.$target", c.p.TypeOf(x.X)), Sel: x.Sel} c.setType(newSel, exprType) c.p.Selections[newSel] = sel return c.formatExpr("(%1e.$ptr_%2s || (%1e.$ptr_%2s = new %3s(function() { return %4e; }, function($v) { %5s }, %1e)))", x.X, x.Sel.Name, c.typeName(exprType), newSel, c.translateAssign(newSel, c.newIdent("$v", exprType), false)) case *ast.IndexExpr: if _, ok := c.p.TypeOf(x.X).Underlying().(*types.Slice); ok { return c.formatExpr("$indexPtr(%1e.$array, %1e.$offset + %2e, %3s)", x.X, x.Index, c.typeName(exprType)) } return c.formatExpr("$indexPtr(%e, %e, %s)", x.X, x.Index, c.typeName(exprType)) case *ast.StarExpr: return c.translateExpr(x.X) default: panic(fmt.Sprintf("Unhandled: %T\n", x)) } case token.ARROW: call := &ast.CallExpr{ Fun: c.newIdent("$recv", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", t)), types.NewTuple(types.NewVar(0, nil, "", exprType), types.NewVar(0, nil, "", types.Typ[types.Bool])), false)), Args: []ast.Expr{e.X}, } c.Blocking[call] = true if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("%e", call) } return c.formatExpr("%e[0]", call) } basic := t.Underlying().(*types.Basic) switch e.Op { case token.ADD: return c.translateExpr(e.X) case token.SUB: switch { case is64Bit(basic): return c.formatExpr("new %1s(-%2h, -%2l)", c.typeName(t), e.X) case isComplex(basic): return c.formatExpr("new %1s(-%2r, -%2i)", c.typeName(t), e.X) case isUnsigned(basic): return c.fixNumber(c.formatExpr("-%e", e.X), basic) default: return c.formatExpr("-%e", e.X) } case token.XOR: if is64Bit(basic) { return c.formatExpr("new %1s(~%2h, ~%2l >>> 0)", c.typeName(t), e.X) } return c.fixNumber(c.formatExpr("~%e", e.X), basic) case token.NOT: return c.formatExpr("!%e", e.X) default: panic(e.Op) } case *ast.BinaryExpr: if e.Op == token.NEQ { return c.formatExpr("!(%s)", c.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } t := c.p.TypeOf(e.X) t2 := c.p.TypeOf(e.Y) _, isInterface := t2.Underlying().(*types.Interface) if isInterface || types.Identical(t, types.Typ[types.UntypedNil]) { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && isNumeric(basic) { if is64Bit(basic) { switch e.Op { case token.MUL: return c.formatExpr("$mul64(%e, %e)", e.X, e.Y) case token.QUO: return c.formatExpr("$div64(%e, %e, false)", e.X, e.Y) case token.REM: return c.formatExpr("$div64(%e, %e, true)", e.X, e.Y) case token.SHL: return c.formatExpr("$shiftLeft64(%e, %f)", e.X, e.Y) case token.SHR: return c.formatExpr("$shiftRight%s(%e, %f)", toJavaScriptType(basic), e.X, e.Y) case token.EQL: return c.formatExpr("(%1h === %2h && %1l === %2l)", e.X, e.Y) case token.LSS: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l < %2l))", e.X, e.Y) case token.LEQ: return c.formatExpr("(%1h < %2h || (%1h === %2h && %1l <= %2l))", e.X, e.Y) case token.GTR: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l > %2l))", e.X, e.Y) case token.GEQ: return c.formatExpr("(%1h > %2h || (%1h === %2h && %1l >= %2l))", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1h %4t %2h, %1l %4t %2l)", e.X, e.Y, c.typeName(t), e.Op) case token.AND, token.OR, token.XOR: return c.formatExpr("new %3s(%1h %4t %2h, (%1l %4t %2l) >>> 0)", e.X, e.Y, c.typeName(t), e.Op) case token.AND_NOT: return c.formatExpr("new %3s(%1h & ~%2h, (%1l & ~%2l) >>> 0)", e.X, e.Y, c.typeName(t)) default: panic(e.Op) } } if isComplex(basic) { switch e.Op { case token.EQL: return c.formatExpr("(%1r === %2r && %1i === %2i)", e.X, e.Y) case token.ADD, token.SUB: return c.formatExpr("new %3s(%1r %4t %2r, %1i %4t %2i)", e.X, e.Y, c.typeName(t), e.Op) case token.MUL: return c.formatExpr("new %3s(%1r * %2r - %1i * %2i, %1r * %2i + %1i * %2r)", e.X, e.Y, c.typeName(t)) case token.QUO: return c.formatExpr("$divComplex(%e, %e)", e.X, e.Y) default: panic(e.Op) } } switch e.Op { case token.EQL: return c.formatParenExpr("%e === %e", e.X, e.Y) case token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.ADD, token.SUB: return c.fixNumber(c.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic) case token.MUL: switch basic.Kind() { case types.Int32: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >> 0) + (%1e << 16 >>> 16) * %2e) >> 0", e.X, e.Y) case types.Uint32, types.Uintptr: return c.formatParenExpr("(((%1e >>> 16 << 16) * %2e >>> 0) + (%1e << 16 >>> 16) * %2e) >>> 0", e.X, e.Y) } return c.fixNumber(c.formatExpr("%e * %e", e.X, e.Y), basic) case token.QUO: if isInteger(basic) { // cut off decimals shift := ">>" if isUnsigned(basic) { shift = ">>>" } return c.formatExpr(`(%1s = %2e / %3e, (%1s === %1s && %1s !== 1/0 && %1s !== -1/0) ? %1s %4s 0 : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_q"), e.X, e.Y, shift) } if basic.Kind() == types.Float32 { return c.fixNumber(c.formatExpr("%e / %e", e.X, e.Y), basic) } return c.formatExpr("%e / %e", e.X, e.Y) case token.REM: return c.formatExpr(`(%1s = %2e %% %3e, %1s === %1s ? %1s : $throwRuntimeError("integer divide by zero"))`, c.newVariable("_r"), e.X, e.Y) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && isUnsigned(basic) { op = ">>>" } if c.p.Types[e.Y].Value != nil { return c.fixNumber(c.formatExpr("%e %s %e", e.X, op, e.Y), basic) } if e.Op == token.SHR && !isUnsigned(basic) { return c.fixNumber(c.formatParenExpr("%e >> $min(%e, 31)", e.X, e.Y), basic) } y := c.newVariable("y") return c.fixNumber(c.formatExpr("(%s = %s, %s < 32 ? (%e %s %s) : 0)", y, c.translateImplicitConversion(e.Y, types.Typ[types.Uint]), y, e.X, op, y), basic) case token.AND, token.OR: if isUnsigned(basic) { return c.formatParenExpr("(%e %t %e) >>> 0", e.X, e.Op, e.Y) } return c.formatParenExpr("%e %t %e", e.X, e.Op, e.Y) case token.AND_NOT: return c.fixNumber(c.formatParenExpr("%e & ~%e", e.X, e.Y), basic) case token.XOR: return c.fixNumber(c.formatParenExpr("%e ^ %e", e.X, e.Y), basic) default: panic(e.Op) } } switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ: return c.formatExpr("%e %t %e", e.X, e.Op, e.Y) case token.LAND: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (!(%s)) { %s = false; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e && %e", e.X, e.Y) case token.LOR: if c.Blocking[e.Y] { skipCase := c.caseCounter c.caseCounter++ resultVar := c.newVariable("_v") c.Printf("if (%s) { %s = true; $s = %d; continue s; }", c.translateExpr(e.X), resultVar, skipCase) c.Printf("%s = %s; case %d:", resultVar, c.translateExpr(e.Y), skipCase) return c.formatExpr("%s", resultVar) } return c.formatExpr("%e || %e", e.X, e.Y) case token.EQL: switch u := t.Underlying().(type) { case *types.Array, *types.Struct: return c.formatExpr("$equal(%e, %e, %s)", e.X, e.Y, c.typeName(t)) case *types.Interface: return c.formatExpr("$interfaceIsEqual(%s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) case *types.Pointer: if _, ok := u.Elem().Underlying().(*types.Array); ok { return c.formatExpr("$equal(%s, %s, %s)", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t), c.typeName(u.Elem())) } case *types.Basic: if isBoolean(u) { if b, ok := analysis.BoolValue(e.X, c.p.Info.Info); ok && b { return c.translateExpr(e.Y) } if b, ok := analysis.BoolValue(e.Y, c.p.Info.Info); ok && b { return c.translateExpr(e.X) } } } return c.formatExpr("%s === %s", c.translateImplicitConversion(e.X, t), c.translateImplicitConversion(e.Y, t)) default: panic(e.Op) } case *ast.ParenExpr: return c.formatParenExpr("%e", e.X) case *ast.IndexExpr: switch t := c.p.TypeOf(e.X).Underlying().(type) { case *types.Array, *types.Pointer: pattern := rangeCheck("%1e[%2f]", c.p.Types[e.Index].Value != nil, true) if _, ok := t.(*types.Pointer); ok { // check pointer for nix (attribute getter causes a panic) pattern = `(%1e.nilCheck, ` + pattern + `)` } return c.formatExpr(pattern, e.X, e.Index) case *types.Slice: return c.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f]", c.p.Types[e.Index].Value != nil, false), e.X, e.Index) case *types.Map: if typesutil.IsJsObject(c.p.TypeOf(e.Index)) { c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: e.Index.Pos(), Msg: "cannot use js.Object as map key"}) } key := fmt.Sprintf("%s.keyFor(%s)", c.typeName(t.Key()), c.translateImplicitConversion(e.Index, t.Key())) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? [%1s.v, true] : [%4e, false])`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) } return c.formatExpr(`(%1s = %2e[%3s], %1s !== undefined ? %1s.v : %4e)`, c.newVariable("_entry"), e.X, key, c.zeroValue(t.Elem())) case *types.Basic: return c.formatExpr("%e.charCodeAt(%f)", e.X, e.Index) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: if b, isBasic := c.p.TypeOf(e.X).Underlying().(*types.Basic); isBasic && isString(b) { switch { case e.Low == nil && e.High == nil: return c.translateExpr(e.X) case e.Low == nil: return c.formatExpr("%e.substring(0, %f)", e.X, e.High) case e.High == nil: return c.formatExpr("%e.substring(%f)", e.X, e.Low) default: return c.formatExpr("%e.substring(%f, %f)", e.X, e.Low, e.High) } } slice := c.translateConversionToSlice(e.X, exprType) switch { case e.Low == nil && e.High == nil: return c.formatExpr("%s", slice) case e.Low == nil: if e.Max != nil { return c.formatExpr("$subslice(%s, 0, %f, %f)", slice, e.High, e.Max) } return c.formatExpr("$subslice(%s, 0, %f)", slice, e.High) case e.High == nil: return c.formatExpr("$subslice(%s, %f)", slice, e.Low) default: if e.Max != nil { return c.formatExpr("$subslice(%s, %f, %f, %f)", slice, e.Low, e.High, e.Max) } return c.formatExpr("$subslice(%s, %f, %f)", slice, e.Low, e.High) } case *ast.SelectorExpr: sel, ok := c.p.Selections[e] if !ok { // qualified identifier return c.formatExpr("%s", c.objectName(obj)) } switch sel.Kind() { case types.FieldVal: fields, jsTag := c.translateSelection(sel, e.Pos()) if jsTag != "" { if _, ok := sel.Type().(*types.Signature); ok { return c.formatExpr("$internalize(%1e.%2s.%3s, %4s, %1e.%2s)", e.X, strings.Join(fields, "."), jsTag, c.typeName(sel.Type())) } return c.internalize(c.formatExpr("%e.%s.%s", e.X, strings.Join(fields, "."), jsTag), sel.Type()) } return c.formatExpr("%e.%s", e.X, strings.Join(fields, ".")) case types.MethodVal: recv := c.makeReceiver(e.X, sel) return c.formatExpr(`$methodVal(%s, "%s")`, recv, sel.Obj().(*types.Func).Name()) case types.MethodExpr: if !sel.Obj().Exported() { c.p.dependencies[sel.Obj()] = true } return c.formatExpr(`$methodExpr(%s, "%s")`, c.typeName(sel.Recv()), sel.Obj().(*types.Func).Name()) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } case *ast.CallExpr: plainFun := astutil.RemoveParens(e.Fun) if astutil.IsTypeExpr(plainFun, c.p.Info.Info) { return c.formatExpr("%s", c.translateConversion(e.Args[0], c.p.TypeOf(plainFun))) } sig := c.p.TypeOf(plainFun).Underlying().(*types.Signature) switch f := plainFun.(type) { case *ast.Ident: obj := c.p.Uses[f] if o, ok := obj.(*types.Builtin); ok { return c.translateBuiltin(o.Name(), sig, e.Args, e.Ellipsis.IsValid()) } if typesutil.IsJsPackage(obj.Pkg()) && obj.Name() == "InternalObject" { return c.translateExpr(e.Args[0]) } return c.translateCall(e, sig, c.translateExpr(f)) case *ast.SelectorExpr: sel, ok := c.p.Selections[f] if !ok { // qualified identifier obj := c.p.Uses[f.Sel] if typesutil.IsJsPackage(obj.Pkg()) { switch obj.Name() { case "Debugger": return c.formatExpr("debugger") case "InternalObject": return c.translateExpr(e.Args[0]) case "MakeFunc": return c.formatExpr("(function() { return $externalize(%e(this, new ($sliceType($jsObjectPtr))($global.Array.prototype.slice.call(arguments, []))), $emptyInterface); })", e.Args[0]) } } return c.translateCall(e, sig, c.translateExpr(f)) } externalizeExpr := func(e ast.Expr) string { t := c.p.TypeOf(e) if types.Identical(t, types.Typ[types.UntypedNil]) { return "null" } return c.externalize(c.translateExpr(e).String(), t) } externalizeArgs := func(args []ast.Expr) string { s := make([]string, len(args)) for i, arg := range args { s[i] = externalizeExpr(arg) } return strings.Join(s, ", ") } switch sel.Kind() { case types.MethodVal: recv := c.makeReceiver(f.X, sel) if typesutil.IsJsPackage(sel.Obj().Pkg()) { globalRef := func(id string) string { if recv.String() == "$global" && id[0] == '$' { return id } return recv.String() + "." + id } switch sel.Obj().Name() { case "Get": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s", globalRef(id)) } return c.formatExpr("%s[$externalize(%e, $String)]", recv, e.Args[0]) case "Set": if id, ok := c.identifierConstant(e.Args[0]); ok { return c.formatExpr("%s = %s", globalRef(id), externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Delete": return c.formatExpr("delete %s[$externalize(%e, $String)]", recv, e.Args[0]) case "Length": return c.formatExpr("$parseInt(%s.length)", recv) case "Index": return c.formatExpr("%s[%e]", recv, e.Args[0]) case "SetIndex": return c.formatExpr("%s[%e] = %s", recv, e.Args[0], externalizeExpr(e.Args[1])) case "Call": if id, ok := c.identifierConstant(e.Args[0]); ok { if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s.%s.apply(%s, %s))", objVar, recv, objVar, id, objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s(%s)", globalRef(id), externalizeArgs(e.Args[1:])) } if e.Ellipsis.IsValid() { objVar := c.newVariable("obj") return c.formatExpr("(%s = %s, %s[$externalize(%e, $String)].apply(%s, %s))", objVar, recv, objVar, e.Args[0], objVar, externalizeExpr(e.Args[1])) } return c.formatExpr("%s[$externalize(%e, $String)](%s)", recv, e.Args[0], externalizeArgs(e.Args[1:])) case "Invoke": if e.Ellipsis.IsValid() { return c.formatExpr("%s.apply(undefined, %s)", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("%s(%s)", recv, externalizeArgs(e.Args)) case "New": if e.Ellipsis.IsValid() { return c.formatExpr("new ($global.Function.prototype.bind.apply(%s, [undefined].concat(%s)))", recv, externalizeExpr(e.Args[0])) } return c.formatExpr("new (%s)(%s)", recv, externalizeArgs(e.Args)) case "Bool": return c.internalize(recv, types.Typ[types.Bool]) case "String": return c.internalize(recv, types.Typ[types.String]) case "Int": return c.internalize(recv, types.Typ[types.Int]) case "Int64": return c.internalize(recv, types.Typ[types.Int64]) case "Uint64": return c.internalize(recv, types.Typ[types.Uint64]) case "Float": return c.internalize(recv, types.Typ[types.Float64]) case "Interface": return c.internalize(recv, types.NewInterface(nil, nil)) case "Unsafe": return recv default: panic("Invalid js package object: " + sel.Obj().Name()) } } methodName := sel.Obj().Name() if reservedKeywords[methodName] { methodName += "$" } return c.translateCall(e, sig, c.formatExpr("%s.%s", recv, methodName)) case types.FieldVal: fields, jsTag := c.translateSelection(sel, f.Pos()) if jsTag != "" { call := c.formatExpr("%e.%s.%s(%s)", f.X, strings.Join(fields, "."), jsTag, externalizeArgs(e.Args)) switch sig.Results().Len() { case 0: return call case 1: return c.internalize(call, sig.Results().At(0).Type()) default: c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: f.Pos(), Msg: "field with js tag can not have func type with multiple results"}) } } return c.translateCall(e, sig, c.formatExpr("%e.%s", f.X, strings.Join(fields, "."))) case types.MethodExpr: return c.translateCall(e, sig, c.translateExpr(f)) default: panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind())) } default: return c.translateCall(e, sig, c.translateExpr(plainFun)) } case *ast.StarExpr: if typesutil.IsJsObject(c.p.TypeOf(e.X)) { return c.formatExpr("new $jsObjectPtr(%e)", e.X) } if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 { if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.Identical(c.p.TypeOf(c2.Fun), types.Typ[types.UnsafePointer]) { if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND { return c.translateExpr(unary.X) // unsafe conversion } } } switch exprType.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } return c.formatExpr("%e.$get()", e.X) case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.p.TypeOf(e.Type) if _, isTuple := exprType.(*types.Tuple); isTuple { return c.formatExpr("$assertType(%e, %s, true)", e.X, c.typeName(t)) } return c.formatExpr("$assertType(%e, %s)", e.X, c.typeName(t)) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := obj.(type) { case *types.Var, *types.Const: return c.formatExpr("%s", c.objectName(o)) case *types.Func: return c.formatExpr("%s", c.objectName(o)) case *types.TypeName: return c.formatExpr("%s", c.typeName(o.Type())) case *types.Nil: if typesutil.IsJsObject(exprType) { return c.formatExpr("null") } switch t := exprType.Underlying().(type) { case *types.Basic: if t.Kind() != types.UnsafePointer { panic("unexpected basic type") } return c.formatExpr("0") case *types.Slice, *types.Pointer, *types.Chan: return c.formatExpr("%s.nil", c.typeName(exprType)) case *types.Map: return c.formatExpr("false") case *types.Interface: return c.formatExpr("$ifaceNil") case *types.Signature: return c.formatExpr("$throwNilPointerError") default: panic(fmt.Sprintf("unexpected type: %T", t)) } default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case *this: if isWrapped(c.p.TypeOf(e)) { return c.formatExpr("this.$val") } return c.formatExpr("this") case nil: return c.formatExpr("") default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
func (sym *symtab) addType(obj types.Object, t types.Type) { fn := sym.typename(t, nil) n := sym.typename(t, sym.pkg) var pkg *types.Package if obj != nil { pkg = obj.Pkg() } id := n if pkg != nil { id = pkg.Name() + "_" + n } kind := skType switch typ := t.(type) { case *types.Basic: kind |= skBasic styp := sym.symtype(typ) if styp == nil { panic(fmt.Errorf("builtin type not already known [%s]!", n)) } case *types.Array: sym.addArrayType(pkg, obj, t, kind, id, n) case *types.Slice: sym.addSliceType(pkg, obj, t, kind, id, n) case *types.Signature: sym.addSignatureType(pkg, obj, t, kind, id, n) case *types.Named: kind |= skNamed switch typ := typ.Underlying().(type) { case *types.Struct: sym.addStructType(pkg, obj, t, kind, id, n) case *types.Basic: bsym := sym.symtype(typ) sym.syms[fn] = &symbol{ gopkg: pkg, goobj: obj, gotyp: t, kind: kind | skBasic, id: id, goname: n, cgoname: "cgo_type_" + id, cpyname: "cpy_type_" + id, pyfmt: bsym.pyfmt, pybuf: bsym.pybuf, pysig: "object", c2py: "cgopy_cnv_c2py_" + id, py2c: "cgopy_cnv_py2c_" + id, pychk: fmt.Sprintf("cpy_func_%[1]s_check(%%s)", id), } case *types.Array: sym.addArrayType(pkg, obj, t, kind, id, n) case *types.Slice: sym.addSliceType(pkg, obj, t, kind, id, n) case *types.Signature: sym.addSignatureType(pkg, obj, t, kind, id, n) case *types.Pointer: sym.addPointerType(pkg, obj, t, kind, id, n) case *types.Interface: sym.addInterfaceType(pkg, obj, t, kind, id, n) default: panic(fmt.Errorf("unhandled named-type: [%T]\n%#v\n", obj, t)) } // add methods for i := 0; i < typ.NumMethods(); i++ { m := typ.Method(i) if !m.Exported() { continue } if true { mid := id + "_" + m.Name() mname := m.Name() sym.addMethod(pkg, m, m.Type(), skFunc, mid, mname) } } case *types.Pointer: sym.addPointerType(pkg, obj, t, kind, id, n) case *types.Map: sym.addMapType(pkg, obj, t, kind, id, n) default: panic(fmt.Errorf("unhandled obj [%T]\ntype [%#v]", obj, t)) } }
// NewDef creates a new Def. func (g *Grapher) NewDef(obj types.Object, declIdent *ast.Ident) (*Def, error) { // Find the AST node that declares this def. var declNode ast.Node _, astPath, _ := g.program.PathEnclosingInterval(declIdent.Pos(), declIdent.End()) for _, node := range astPath { switch node.(type) { case *ast.FuncDecl, *ast.GenDecl, *ast.ValueSpec, *ast.TypeSpec, *ast.Field, *ast.DeclStmt, *ast.AssignStmt: declNode = node goto found } } found: if declNode == nil { return nil, fmt.Errorf("On ident %s at %s: no DeclNode found (using PathEnclosingInterval)", declIdent.Name, g.program.Fset.Position(declIdent.Pos())) } key, info, err := g.defInfo(obj) if err != nil { return nil, err } si := definfo.DefInfo{ Exported: info.exported, PkgScope: info.pkgscope, PkgName: obj.Pkg().Name(), Kind: defKind(obj), } if typ := obj.Type(); typ != nil { si.TypeString = typ.String() if utyp := typ.Underlying(); utyp != nil { si.UnderlyingTypeString = utyp.String() } } switch obj := obj.(type) { case *types.Var: if obj.IsField() { if fieldStruct, ok := g.structFields[obj]; ok { if struct_, ok := fieldStruct.parent.(*types.Named); ok { si.FieldOfStruct = struct_.Obj().Name() } } } case *types.Func: sig := obj.Type().(*types.Signature) if recv := sig.Recv(); recv != nil && recv.Type() != nil { // omit package path; just get receiver type name si.Receiver = strings.Replace(recv.Type().String(), obj.Pkg().Path()+".", "", 1) } } return &Def{ Name: obj.Name(), DefKey: key, File: g.program.Fset.Position(declIdent.Pos()).Filename, IdentSpan: makeSpan(g.program.Fset, declIdent), DeclSpan: makeSpan(g.program.Fset, declNode), DefInfo: si, }, nil }
// packageLevelValue returns the package-level value corresponding to // the specified named object, which may be a package-level const // (*Const), var (*Global) or func (*Function) of some package in // prog. It returns nil if the object is not found. // func (prog *Program) packageLevelValue(obj types.Object) Value { if pkg, ok := prog.packages[obj.Pkg()]; ok { return pkg.values[obj] } return nil }
func (o *Object) setParent(obj types.Object) { if obj != nil { o.Parent = obj.Name() o.setPkg(obj.Pkg()) } }
func actionDoc(s *Session, in string) error { s.clearQuickFix() s.storeMainBody() defer s.restoreMainBody() expr, err := s.evalExpr(in) if err != nil { return err } s.TypeInfo = types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Uses: make(map[*ast.Ident]types.Object), Defs: make(map[*ast.Ident]types.Object), Scopes: make(map[ast.Node]*types.Scope), } _, err = s.Types.Check("_tmp", s.Fset, []*ast.File{s.File}, &s.TypeInfo) if err != nil { debugf("typecheck error (ignored): %s", err) } // :doc patterns: // - "json" -> "encoding/json" (package name) // - "json.Encoder" -> "encoding/json", "Encoder" (package member) // - "json.NewEncoder(nil).Encode" -> "encoding/json", "Decode" (package type member) var docObj types.Object if sel, ok := expr.(*ast.SelectorExpr); ok { // package member, package type member docObj = s.TypeInfo.ObjectOf(sel.Sel) } else if t := s.TypeInfo.TypeOf(expr); t != nil && t != types.Typ[types.Invalid] { for { if pt, ok := t.(*types.Pointer); ok { t = pt.Elem() } else { break } } switch t := t.(type) { case *types.Named: docObj = t.Obj() case *types.Basic: // builtin types docObj = types.Universe.Lookup(t.Name()) } } else if ident, ok := expr.(*ast.Ident); ok { // package name mainScope := s.TypeInfo.Scopes[s.mainFunc().Type] _, docObj = mainScope.LookupParent(ident.Name, ident.NamePos) } if docObj == nil { return fmt.Errorf("cannot determine the document location") } debugf("doc :: obj=%#v", docObj) var pkgPath, objName string if pkgName, ok := docObj.(*types.PkgName); ok { pkgPath = pkgName.Imported().Path() } else { if pkg := docObj.Pkg(); pkg != nil { pkgPath = pkg.Path() } else { pkgPath = "builtin" } objName = docObj.Name() } debugf("doc :: %q %q", pkgPath, objName) args := []string{pkgPath} if objName != "" { args = append(args, objName) } godoc := exec.Command("godoc", args...) godoc.Stderr = os.Stderr // TODO just use PAGER? if pagerCmd := os.Getenv("GORE_PAGER"); pagerCmd != "" { r, err := godoc.StdoutPipe() if err != nil { return err } pager := exec.Command(pagerCmd) pager.Stdin = r pager.Stdout = os.Stdout pager.Stderr = os.Stderr err = pager.Start() if err != nil { return err } err = godoc.Run() if err != nil { return err } return pager.Wait() } else { godoc.Stdout = os.Stdout return godoc.Run() } }
func isPackageLevel(obj types.Object) bool { return obj.Pkg().Scope().Lookup(obj.Name()) == obj }
func isAccessibleFrom(obj types.Object, pkg *types.Package) bool { return ast.IsExported(obj.Name()) || obj.Pkg() == pkg }
func (w *PkgWalker) LookupObjects(pkg *types.Package, pkgInfo *types.Info, cursor *FileCursor) { var cursorObj types.Object var cursorSelection *types.Selection var cursorObjIsDef bool //lookup defs _ = 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() { if named, ok := cursorObj.Type().(*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 { named := cursorSelection.Recv().(*types.Named) 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 } } 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 { for _, obj := range conf.Info.Defs { if obj != nil && obj.String() == cursorObj.String() { cursorPos = obj.Pos() break } } } } if kind == ObjField || cursorIsInterfaceMethod { fieldTypeInfo = conf.Info } } if kind == ObjField { fieldTypeObj = w.LookupStructFromField(fieldTypeInfo, cursorPkg, cursorObj, cursorPos) } if typeFindDef { fmt.Println(w.fset.Position(cursorPos)) } if typeFindInfo { if kind == ObjField && fieldTypeObj != nil { typeName := fieldTypeObj.Name() if fieldTypeObj.Pkg() != nil && fieldTypeObj.Pkg() != pkg { typeName = fieldTypeObj.Pkg().Name() + "." + fieldTypeObj.Name() } fmt.Println(typeName, simpleType(cursorObj.String())) } else if kind == ObjBuiltin { fmt.Println(builtinInfo(cursorObj.Name())) } else if kind == ObjPkgName { fmt.Println(cursorObj.String()) } else if cursorIsInterfaceMethod { fmt.Println(strings.Replace(simpleType(cursorObj.String()), "(interface)", cursorPkg.Name()+"."+cursorInterfaceTypeName, 1)) } else { fmt.Println(simpleType(cursorObj.String())) } } //if f, ok := w.parsedFileCache[w.fset.Position(cursorPos).Filename]; ok { // for _, d := range f.Decls { // if inRange(d, cursorPos) { // if fd, ok := d.(*ast.FuncDecl); ok { // fd.Body = nil // } // commentMap := ast.NewCommentMap(w.fset, f, f.Comments) // commentedNode := printer.CommentedNode{Node: d} // if comments := commentMap.Filter(d).Comments(); comments != nil { // commentedNode.Comments = comments // } // var b bytes.Buffer // printer.Fprint(&b, w.fset, &commentedNode) // b.Write([]byte("\n\n")) // Add a blank line between entries if we print documentation. // log.Println(w.nodeString(d)) // } // } //} if !typeFindUse { 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())) } } } (sort.IntSlice(usages)).Sort() for _, pos := range usages { fmt.Println(w.fset.Position(token.Pos(pos))) } }