// 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) }
// 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()] lexinfo := lexical.Structure(r.iprog.Fset, from.Pkg(), &info.Info, info.Files) // 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. if refs := lexinfo.Refs[from]; len(refs) > 0 { r.errorf(from.Pos(), "renaming this func %q to %q would make it a package initializer", from.Name(), r.to) r.errorf(refs[0].Id.Pos(), "\tbut references to it exist") } } 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 { if prev, b := lexinfo.Blocks[f].Lookup(r.to); b == lexinfo.Blocks[f] { 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 (r *Unexporter) lexInfo(info *loader.PackageInfo) *lexical.Info { r.mutex.Lock() defer r.mutex.Unlock() if lexinfo := r.lexinfos[info]; lexinfo != nil { return lexinfo } else { lexinfo := lexical.Structure(r.iprog.Fset, info.Pkg, &info.Info, info.Files) r.lexinfos[info] = lexinfo return lexinfo } }
func (e *Export) checkInLexicalScope(from types.Object, to string) { info := e.u.pkgInfo lexinfo := lexical.Structure(e.u.prog.Fset, info.Pkg, &info.Info, info.Files) b := lexinfo.Defs[from] // the block defining the 'from' object if b != nil { to, toBlock := b.Lookup(to) if toBlock == b { e.Conflicting = true return // same-block conflict } else if toBlock != nil { for _, ref := range lexinfo.Refs[to] { if obj, _ := ref.Env.Lookup(from.Name()); obj == from { e.Conflicting = true return // super-block conflict } } } } // Check for sub-block conflict. // Is there an intervening definition of r.to between // the block defining 'from' and some reference to it? for _, ref := range lexinfo.Refs[from] { _, 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 { e.Conflicting = true 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 { e.check(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) { lexinfo := lexical.Structure(r.iprog.Fset, info.Pkg, &info.Info, info.Files) b := lexinfo.Defs[from] // the block defining the 'from' object if b != nil { to, toBlock := b.Lookup(r.to) 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? for _, ref := range lexinfo.Refs[to] { if obj, _ := ref.Env.Lookup(from.Name()); obj == from { // super-block conflict r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.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 r.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(r.to) if to != nil { // sub-block conflict if toBlock.Depth() > fromDepth { r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.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(field) } } } } }