func (f *Function) addParamObj(obj types.Object) *Parameter { name := obj.Name() if name == "" { name = fmt.Sprintf("arg%d", len(f.Params)) } param := f.addParam(name, obj.Type(), obj.Pos()) param.object = obj return param }
func (v *visitor) decl(obj types.Object) { key := getKey(obj) if _, ok := v.uses[key]; !ok { v.uses[key] = 0 } if _, ok := v.positions[key]; !ok { v.positions[key] = v.prog.Fset.Position(obj.Pos()) } }
// memberFromObject populates package pkg with a member for the // typechecker object obj. // // For objects from Go source code, syntax is the associated syntax // tree (for funcs and vars only); it will be used during the build // phase. // func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { name := obj.Name() switch obj := obj.(type) { case *types.TypeName: pkg.Members[name] = &Type{ object: obj, pkg: pkg, } case *types.Const: c := &NamedConst{ object: obj, Value: NewConst(obj.Val(), obj.Type()), pkg: pkg, } pkg.values[obj] = c.Value pkg.Members[name] = c case *types.Var: g := &Global{ Pkg: pkg, name: name, object: obj, typ: types.NewPointer(obj.Type()), // address pos: obj.Pos(), } pkg.values[obj] = g pkg.Members[name] = g case *types.Func: sig := obj.Type().(*types.Signature) if sig.Recv() == nil && name == "init" { pkg.ninit++ name = fmt.Sprintf("init#%d", pkg.ninit) } fn := &Function{ name: name, object: obj, Signature: sig, syntax: syntax, pos: obj.Pos(), Pkg: pkg, Prog: pkg.Prog, } if syntax == nil { fn.Synthetic = "loaded from gc object file" } pkg.values[obj] = fn if sig.Recv() == nil { pkg.Members[name] = fn // package-level function } default: // (incl. *types.Package) panic("unexpected Object type: " + obj.String()) } }
// addSpilledParam declares a parameter that is pre-spilled to the // stack; the function body will load/store the spilled location. // Subsequent lifting will eliminate spills where possible. // func (f *Function) addSpilledParam(obj types.Object) { param := f.addParamObj(obj) spill := &Alloc{Comment: obj.Name()} spill.setType(types.NewPointer(obj.Type())) spill.setPos(obj.Pos()) f.objects[obj] = spill f.Locals = append(f.Locals, spill) f.emit(spill) f.emit(&Store{Addr: spill, Val: param}) }
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 *Unexporter) selectionConflict(objsToUpdate map[types.Object]string, from types.Object, to string, delta int, syntax *ast.SelectorExpr, obj types.Object) { rename := r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), to) switch { case delta < 0: // analogous to sub-block conflict r.warn(from, rename, r.errorf(syntax.Sel.Pos(), "\twould change the referent of this selection"), r.errorf(obj.Pos(), "\tof this %s", objectKind(obj))) case delta == 0: // analogous to same-block conflict r.warn(from, rename, r.errorf(syntax.Sel.Pos(), "\twould make this reference ambiguous"), r.errorf(obj.Pos(), "\twith this %s", objectKind(obj))) case delta > 0: // analogous to super-block conflict r.warn(from, rename, r.errorf(syntax.Sel.Pos(), "\twould shadow this selection"), r.errorf(obj.Pos(), "\tof the %s declared here", objectKind(obj))) } }
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 (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 (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...] } }
// 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) } }
// 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) } }
// check performs safety checks of the renaming of the 'from' object to r.to. func (r *renamer) check(from types.Object) { if r.objsToUpdate[from] { return } r.objsToUpdate[from] = true // NB: order of conditions is important. if from_, ok := from.(*types.PkgName); ok { r.checkInFileBlock(from_) } else if from_, ok := from.(*types.Label); ok { r.checkLabel(from_) } else if isPackageLevel(from) { r.checkInPackageBlock(from) } else if v, ok := from.(*types.Var); ok && v.IsField() { r.checkStructField(v) } else if f, ok := from.(*types.Func); ok && recv(f) != nil { r.checkMethod(f) } else if isLocal(from) { r.checkInLocalScope(from) } else { r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n", objectKind(from), from) } }
func (r *Unexporter) check(objsToUpdate map[types.Object]string, from types.Object, to string) { if _, ok := objsToUpdate[from]; ok { return } objsToUpdate[from] = to // NB: order of conditions is important. if from_, ok := from.(*types.PkgName); ok { r.checkInFileBlock(objsToUpdate, from_, to) } else if from_, ok := from.(*types.Label); ok { r.checkLabel(from_, to) } else if isPackageLevel(from) { r.checkInPackageBlock(objsToUpdate, from, to) } else if v, ok := from.(*types.Var); ok && v.IsField() { r.checkStructField(objsToUpdate, v, to) } else if f, ok := from.(*types.Func); ok && recv(f) != nil { r.checkMethod(objsToUpdate, f, to) } else if isLocal(from) { r.checkInLocalScope(objsToUpdate, from, to) } else { r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n", objectKind(from), from) } }
func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) { r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) switch { case delta < 0: // analogous to sub-block conflict r.errorf(syntax.Sel.Pos(), "\twould change the referent of this selection") r.errorf(obj.Pos(), "\tto this %s", objectKind(obj)) case delta == 0: // analogous to same-block conflict r.errorf(syntax.Sel.Pos(), "\twould make this reference ambiguous") r.errorf(obj.Pos(), "\twith this %s", objectKind(obj)) case delta > 0: // analogous to super-block conflict r.errorf(syntax.Sel.Pos(), "\twould shadow this selection") r.errorf(obj.Pos(), "\tto the %s declared here", objectKind(obj)) } }
func (g *Grapher) path(obj types.Object) (path []string) { if path, present := g.paths[obj]; present { return path } var scope *types.Scope pkgInfo, astPath, _ := g.program.PathEnclosingInterval(obj.Pos(), obj.Pos()) if astPath != nil { for _, node := range astPath { if s, hasScope := pkgInfo.Scopes[node]; hasScope { scope = s } } } if scope == nil { scope = obj.Parent() } if scope == nil { // TODO(sqs): make this actually handle cases like the one described in // https://github.com/sourcegraph/sourcegraph.com/issues/218 log.Printf("Warning: no scope for object %s at pos %s", obj.String(), g.program.Fset.Position(obj.Pos())) return nil } prefix, hasPath := g.scopePaths[scope] if !hasPath { panic("no scope path for scope " + scope.String()) } path = append([]string{}, prefix...) p := g.program.Fset.Position(obj.Pos()) path = append(path, obj.Name()+uniqID(p)) return path panic("no scope node for object " + obj.String()) }
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))) } }
// checkInLexicalScope performs safety checks that a renaming does not // change the lexical reference structure of the specified package. // // For objects in lexical scope, there are three kinds of conflicts: // same-, sub-, and super-block conflicts. We will illustrate all three // using this example: // // var x int // var z int // // func f(y int) { // print(x) // print(y) // } // // Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object // with the new name already exists, defined in the same lexical block // as the old object. // // Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists // a reference to x from within (what would become) a hole in its scope. // The definition of y in an (inner) sub-block would cast a shadow in // the scope of the renamed variable. // // Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the // converse situation: there is an existing definition of the new name // (x) in an (enclosing) super-block, and the renaming would create a // hole in its scope, within which there exist references to it. The // new name casts a shadow in scope of the existing definition of x in // the super-block. // // Removing the old name (and all references to it) is always safe, and // requires no checks. // func (r *Unexporter) checkInLexicalScope(objsToUpdate map[types.Object]string, from types.Object, to string, info *loader.PackageInfo) { lexinfo := r.lexInfo(info) b := lexinfo.Defs[from] // the block defining the 'from' object if b != nil { to, toBlock := b.Lookup(to) if toBlock == b { // same-block conflict r.warn(from, r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), to), r.errorf(to.Pos(), "\tconflicts with %s in same block", objectKind(to))) return } else if toBlock != nil { // Check for super-block conflict. // The name to is defined in a superblock. // Is that name referenced from within this block? for _, ref := range lexinfo.Refs[to] { if obj, _ := ref.Env.Lookup(from.Name()); obj == from { // super-block conflict r.warn(from, r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), to), r.errorf(ref.Id.Pos(), "\twould shadow this reference"), r.errorf(to.Pos(), "\tto the %s declared here", objectKind(to))) return } } } } // Check for sub-block conflict. // Is there an intervening definition of to between // the block defining 'from' and some reference to it? for _, ref := range lexinfo.Refs[from] { // TODO(adonovan): think about dot imports. // (Is b == fromBlock an invariant?) _, fromBlock := ref.Env.Lookup(from.Name()) fromDepth := fromBlock.Depth() to, toBlock := ref.Env.Lookup(to) if to != nil { // sub-block conflict if toBlock.Depth() > fromDepth { r.warn(from, r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), to), r.errorf(ref.Id.Pos(), "\twould cause this reference to become shadowed"), r.errorf(to.Pos(), "\tby this intervening %s definition", objectKind(to))) return } } } // Renaming a type that is used as an embedded field // requires renaming the field too. e.g. // type T int // if we rename this to U.. // var s struct {T} // print(s.T) // ...this must change too if _, ok := from.(*types.TypeName); ok { for id, obj := range info.Uses { if obj == from { if field := info.Defs[id]; field != nil { r.check(objsToUpdate, field, to) } } } } }
// checkInLexicalScope performs safety checks that a renaming does not // change the lexical reference structure of the specified package. // // For objects in lexical scope, there are three kinds of conflicts: // same-, sub-, and super-block conflicts. We will illustrate all three // using this example: // // var x int // var z int // // func f(y int) { // print(x) // print(y) // } // // Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object // with the new name already exists, defined in the same lexical block // as the old object. // // Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists // a reference to x from within (what would become) a hole in its scope. // The definition of y in an (inner) sub-block would cast a shadow in // the scope of the renamed variable. // // Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the // converse situation: there is an existing definition of the new name // (x) in an (enclosing) super-block, and the renaming would create a // hole in its scope, within which there exist references to it. The // new name casts a shadow in scope of the existing definition of x in // the super-block. // // Removing the old name (and all references to it) is always safe, and // requires no checks. // func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) { b := from.Parent() // the block defining the 'from' object if b != nil { toBlock, to := b.LookupParent(r.to, from.Parent().End()) if toBlock == b { // same-block conflict r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(to.Pos(), "\tconflicts with %s in same block", objectKind(to)) return } else if toBlock != nil { // Check for super-block conflict. // The name r.to is defined in a superblock. // Is that name referenced from within this block? forEachLexicalRef(info, to, func(id *ast.Ident, block *types.Scope) bool { _, obj := lexicalLookup(block, from.Name(), id.Pos()) if obj == from { // super-block conflict r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(id.Pos(), "\twould shadow this reference") r.errorf(to.Pos(), "\tto the %s declared here", objectKind(to)) return false // stop } return true }) } } // Check for sub-block conflict. // Is there an intervening definition of r.to between // the block defining 'from' and some reference to it? forEachLexicalRef(info, from, func(id *ast.Ident, block *types.Scope) bool { // Find the block that defines the found reference. // It may be an ancestor. fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos()) // See what r.to would resolve to in the same scope. toBlock, to := lexicalLookup(block, r.to, id.Pos()) if to != nil { // sub-block conflict if deeper(toBlock, fromBlock) { r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(id.Pos(), "\twould cause this reference to become shadowed") r.errorf(to.Pos(), "\tby this intervening %s definition", objectKind(to)) return false // stop } } return true }) // Renaming a type that is used as an embedded field // requires renaming the field too. e.g. // type T int // if we rename this to U.. // var s struct {T} // print(s.T) // ...this must change too if _, ok := from.(*types.TypeName); ok { for id, obj := range info.Uses { if obj == from { if field := info.Defs[id]; field != nil { r.check(field) } } } } }
// addNamedLocal creates a local variable, adds it to function f and // returns it. Its name and type are taken from obj. Subsequent // calls to f.lookup(obj) will return the same local. // func (f *Function) addNamedLocal(obj types.Object) *Alloc { l := f.addLocal(obj.Type(), obj.Pos()) l.Comment = obj.Name() f.objects[obj] = l return l }
// PositionForObject returns the position for an object as token.Position func (u *Unexporter) PositionForObject(o types.Object) token.Position { return u.prog.Fset.PositionFor(o.Pos(), true) }
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))) } } }