//getFullName is returning unique name of obj. func getFullName(obj types.Object, ctx *getDefinitionsContext, isType bool) string { if obj == nil { return "" } if isType { return obj.Type().String() } result := "" switch obj.(type) { case *types.Func: f := obj.(*types.Func) r := strings.NewReplacer("(", "", "*", "", ")", "") result = r.Replace(f.FullName()) default: if obj.Pkg() != nil { result += obj.Pkg().Path() result += "." } if packageName, ok := ctx.structs[posToStr(ctx.fset, obj.Pos())]; ok { result += packageName result += "." } result += obj.Name() } return result }
func (p *exporter) fileLine(obj types.Object) (file string, line int) { if p.fset != nil { pos := p.fset.Position(obj.Pos()) file = pos.Filename line = pos.Line } return }
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()) } }
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 }
// 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 (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 printTargetObj(obj types.Object) { if obj != nil && obj.Pos() != token.NoPos { fmt.Println(posPrinter{obj}) return } if *godef != "" { cmd := exec.Command(*godef, os.Args[1:]...) cmd.Stdin = bytes.NewReader(fileBody) b, err := cmd.Output() if err != nil { fail() } os.Stdout.Write(b) return } fail() }
func (r *renamer) checkInLocalScope(from types.Object) { 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(obj) } } r.checkInLexicalScope(from, 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 = r.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) } }
// 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 saveRecent(ident *ast.Ident, obj types.Object) { if *cacheFile == "" { return } if obj == nil || obj.Pos() == token.NoPos { return } if fset.Position(ident.Pos()).Filename == fset.Position(obj.Pos()).Filename { lg("same file") return } k := posPrinter{ident}.String() sha := fileSHA(fset.Position(obj.Pos()).Filename) if sha == "" { return } now := time.Now() ent := &objectEntry{ FromSHA1: fileSHA1, ToPos: posPrinter{obj}.String(), ToSHA1: sha, Created: now.UnixNano(), } lg("new recent entry %s => %+v", k, ent) if recents == nil { recents = &recentObjects{entries: make(map[string]*objectEntry)} } recents.entries[k] = ent expire := now.Add(-24 * time.Hour).UnixNano() for k, ent := range recents.entries { if ent.bad || ent.Created < expire { delete(recents.entries, k) } } b, err := json.MarshalIndent(recents.entries, "", " ") if err != nil { lg("marshal recents err=%v", err) return } if err = ioutil.WriteFile(*cacheFile, b, 0600); err != nil { lg("write recents err=%v", err) } }
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(), "\tof 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(), "\tof the %s declared here", objectKind(obj)) } }
func fileLine(fset *token.FileSet, obj types.Object) string { posn := fset.Position(obj.Pos()) return fmt.Sprintf("%s:%d", posn.Filename, posn.Line) }
func formatNode(n ast.Node, obj types.Object, prog *loader.Program) string { var nc ast.Node // Render a copy of the node with no documentation. // We emit the documentation ourself. switch n := n.(type) { case *ast.FuncDecl: cp := *n cp.Doc = nil // Don't print the whole function body cp.Body = nil nc = &cp case *ast.GenDecl: cp := *n cp.Doc = nil if len(n.Specs) > 0 { // Only print this one type, not all the types in the // gendecl switch n.Specs[0].(type) { case *ast.TypeSpec: spec := findTypeSpec(n, obj.Pos()) if spec != nil { specCp := *spec if *showUnexportedFields == false { trimUnexportedElems(&specCp) } specCp.Doc = nil cp.Specs = []ast.Spec{&specCp} } cp.Lparen = 0 cp.Rparen = 0 case *ast.ValueSpec: spec := findVarSpec(n, obj.Pos()) if spec != nil { specCp := *spec specCp.Doc = nil cp.Specs = []ast.Spec{&specCp} } cp.Lparen = 0 cp.Rparen = 0 } } nc = &cp case *ast.Field: // Not supported by go/printer // TODO(dominikh): Methods in interfaces are syntactically // represented as fields. Using types.Object.String for those // causes them to look different from real functions. // go/printer doesn't include the import paths in names, while // Object.String does. Fix that. return obj.String() default: return obj.String() } buf := &bytes.Buffer{} cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} err := cfg.Fprint(buf, prog.Fset, nc) if err != nil { return obj.String() } return buf.String() }
// 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 }
// 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) } } } } }