// 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) } }
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() }
// 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 }