1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
7 // This file defines the safety checks for each kind of renaming.
15 "golang.org/x/tools/go/loader"
16 "golang.org/x/tools/refactor/satisfy"
19 // errorf reports an error (e.g. conflict) and prevents file modification.
20 func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) {
22 reportError(r.iprog.Fset.Position(pos), fmt.Sprintf(format, args...))
25 // check performs safety checks of the renaming of the 'from' object to r.to.
26 func (r *renamer) check(from types.Object) {
27 if r.objsToUpdate[from] {
30 r.objsToUpdate[from] = true
32 // NB: order of conditions is important.
33 if from_, ok := from.(*types.PkgName); ok {
34 r.checkInFileBlock(from_)
35 } else if from_, ok := from.(*types.Label); ok {
37 } else if isPackageLevel(from) {
38 r.checkInPackageBlock(from)
39 } else if v, ok := from.(*types.Var); ok && v.IsField() {
41 } else if f, ok := from.(*types.Func); ok && recv(f) != nil {
43 } else if isLocal(from) {
44 r.checkInLocalScope(from)
46 r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n",
47 objectKind(from), from)
51 // checkInFileBlock performs safety checks for renames of objects in the file block,
52 // i.e. imported package names.
53 func (r *renamer) checkInFileBlock(from *types.PkgName) {
54 // Check import name is not "init".
56 r.errorf(from.Pos(), "%q is not a valid imported package name", r.to)
59 // Check for conflicts between file and package block.
60 if prev := from.Pkg().Scope().Lookup(r.to); prev != nil {
61 r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
62 objectKind(from), from.Name(), r.to)
63 r.errorf(prev.Pos(), "\twith this package member %s",
65 return // since checkInPackageBlock would report redundant errors
68 // Check for conflicts in lexical scope.
69 r.checkInLexicalScope(from, r.packages[from.Pkg()])
71 // Finally, modify ImportSpec syntax to add or remove the Name as needed.
72 info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
73 if from.Imported().Name() == r.to {
74 // ImportSpec.Name not needed
75 path[1].(*ast.ImportSpec).Name = nil
77 // ImportSpec.Name needed
78 if spec := path[1].(*ast.ImportSpec); spec.Name == nil {
79 spec.Name = &ast.Ident{NamePos: spec.Path.Pos(), Name: r.to}
80 info.Defs[spec.Name] = from
85 // checkInPackageBlock performs safety checks for renames of
86 // func/var/const/type objects in the package block.
87 func (r *renamer) checkInPackageBlock(from types.Object) {
88 // Check that there are no references to the name from another
89 // package if the renaming would make it unexported.
90 if ast.IsExported(from.Name()) && !ast.IsExported(r.to) {
91 for pkg, info := range r.packages {
92 if pkg == from.Pkg() {
95 if id := someUse(info, from); id != nil &&
96 !r.checkExport(id, pkg, from) {
102 info := r.packages[from.Pkg()]
104 // Check that in the package block, "init" is a function, and never referenced.
106 kind := objectKind(from)
108 // Reject if intra-package references to it exist.
109 for id, obj := range info.Uses {
112 "renaming this func %q to %q would make it a package initializer",
114 r.errorf(id.Pos(), "\tbut references to it exist")
119 r.errorf(from.Pos(), "you cannot have a %s at package level named %q",
124 // Check for conflicts between package block and all file blocks.
125 for _, f := range info.Files {
126 fileScope := info.Info.Scopes[f]
127 b, prev := fileScope.LookupParent(r.to, token.NoPos)
129 r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
130 objectKind(from), from.Name(), r.to)
131 r.errorf(prev.Pos(), "\twith this %s",
133 return // since checkInPackageBlock would report redundant errors
137 // Check for conflicts in lexical scope.
139 for _, info := range r.packages {
140 r.checkInLexicalScope(from, info)
143 r.checkInLexicalScope(from, info)
147 func (r *renamer) checkInLocalScope(from types.Object) {
148 info := r.packages[from.Pkg()]
150 // Is this object an implicit local var for a type switch?
151 // Each case has its own var, whose position is the decl of y,
152 // but Ident in that decl does not appear in the Uses map.
154 // switch y := x.(type) { // Defs[Ident(y)] is undefined
155 // case int: print(y) // Implicits[CaseClause(int)] = Var(y_int)
156 // case string: print(y) // Implicits[CaseClause(string)] = Var(y_string)
160 for syntax, obj := range info.Implicits {
161 if _, ok := syntax.(*ast.CaseClause); ok && obj.Pos() == from.Pos() {
167 r.checkInLexicalScope(from, info)
169 // Finally, if this was a type switch, change the variable y.
171 _, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
172 path[0].(*ast.Ident).Name = r.to // path is [Ident AssignStmt TypeSwitchStmt...]
176 // checkInLexicalScope performs safety checks that a renaming does not
177 // change the lexical reference structure of the specified package.
179 // For objects in lexical scope, there are three kinds of conflicts:
180 // same-, sub-, and super-block conflicts. We will illustrate all three
181 // using this example:
191 // Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object
192 // with the new name already exists, defined in the same lexical block
193 // as the old object.
195 // Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists
196 // a reference to x from within (what would become) a hole in its scope.
197 // The definition of y in an (inner) sub-block would cast a shadow in
198 // the scope of the renamed variable.
200 // Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the
201 // converse situation: there is an existing definition of the new name
202 // (x) in an (enclosing) super-block, and the renaming would create a
203 // hole in its scope, within which there exist references to it. The
204 // new name casts a shadow in scope of the existing definition of x in
207 // Removing the old name (and all references to it) is always safe, and
208 // requires no checks.
210 func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) {
211 b := from.Parent() // the block defining the 'from' object
213 toBlock, to := b.LookupParent(r.to, from.Parent().End())
215 // same-block conflict
216 r.errorf(from.Pos(), "renaming this %s %q to %q",
217 objectKind(from), from.Name(), r.to)
218 r.errorf(to.Pos(), "\tconflicts with %s in same block",
221 } else if toBlock != nil {
222 // Check for super-block conflict.
223 // The name r.to is defined in a superblock.
224 // Is that name referenced from within this block?
225 forEachLexicalRef(info, to, func(id *ast.Ident, block *types.Scope) bool {
226 _, obj := lexicalLookup(block, from.Name(), id.Pos())
228 // super-block conflict
229 r.errorf(from.Pos(), "renaming this %s %q to %q",
230 objectKind(from), from.Name(), r.to)
231 r.errorf(id.Pos(), "\twould shadow this reference")
232 r.errorf(to.Pos(), "\tto the %s declared here",
241 // Check for sub-block conflict.
242 // Is there an intervening definition of r.to between
243 // the block defining 'from' and some reference to it?
244 forEachLexicalRef(info, from, func(id *ast.Ident, block *types.Scope) bool {
245 // Find the block that defines the found reference.
246 // It may be an ancestor.
247 fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos())
249 // See what r.to would resolve to in the same scope.
250 toBlock, to := lexicalLookup(block, r.to, id.Pos())
252 // sub-block conflict
253 if deeper(toBlock, fromBlock) {
254 r.errorf(from.Pos(), "renaming this %s %q to %q",
255 objectKind(from), from.Name(), r.to)
256 r.errorf(id.Pos(), "\twould cause this reference to become shadowed")
257 r.errorf(to.Pos(), "\tby this intervening %s definition",
265 // Renaming a type that is used as an embedded field
266 // requires renaming the field too. e.g.
267 // type T int // if we rename this to U..
269 // print(s.T) // ...this must change too
270 if _, ok := from.(*types.TypeName); ok {
271 for id, obj := range info.Uses {
273 if field := info.Defs[id]; field != nil {
281 // lexicalLookup is like (*types.Scope).LookupParent but respects the
282 // environment visible at pos. It assumes the relative position
283 // information is correct with each file.
284 func lexicalLookup(block *types.Scope, name string, pos token.Pos) (*types.Scope, types.Object) {
285 for b := block; b != nil; b = b.Parent() {
286 obj := b.Lookup(name)
287 // The scope of a package-level object is the entire package,
288 // so ignore pos in that case.
289 // No analogous clause is needed for file-level objects
290 // since no reference can appear before an import decl.
291 if obj != nil && (b == obj.Pkg().Scope() || obj.Pos() < pos) {
298 // deeper reports whether block x is lexically deeper than y.
299 func deeper(x, y *types.Scope) bool {
300 if x == y || x == nil {
305 return deeper(x.Parent(), y.Parent())
309 // forEachLexicalRef calls fn(id, block) for each identifier id in package
310 // info that is a reference to obj in lexical scope. block is the
311 // lexical block enclosing the reference. If fn returns false the
312 // iteration is terminated and findLexicalRefs returns false.
313 func forEachLexicalRef(info *loader.PackageInfo, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool {
317 var visit func(n ast.Node) bool
318 visit = func(n ast.Node) bool {
320 stack = stack[:len(stack)-1] // pop
324 return false // bail out
327 stack = append(stack, n) // push
328 switch n := n.(type) {
330 if info.Uses[n] == obj {
331 block := enclosingBlock(&info.Info, stack)
336 return visit(nil) // pop stack
338 case *ast.SelectorExpr:
340 ast.Inspect(n.X, visit)
341 return visit(nil) // pop stack, don't descend
343 case *ast.CompositeLit:
344 // Handle recursion ourselves for struct literals
345 // so we don't visit field identifiers.
347 if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
349 ast.Inspect(n.Type, visit)
351 for _, elt := range n.Elts {
352 if kv, ok := elt.(*ast.KeyValueExpr); ok {
353 ast.Inspect(kv.Value, visit)
355 ast.Inspect(elt, visit)
358 return visit(nil) // pop stack, don't descend
364 for _, f := range info.Files {
365 ast.Inspect(f, visit)
376 // enclosingBlock returns the innermost block enclosing the specified
377 // AST node, specified in the form of a path from the root of the file,
379 func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope {
380 for i := range stack {
381 n := stack[len(stack)-1-i]
382 // For some reason, go/types always associates a
383 // function's scope with its FuncType.
384 // TODO(adonovan): feature or a bug?
385 switch f := n.(type) {
391 if b := info.Scopes[n]; b != nil {
395 panic("no Scope for *ast.File")
398 func (r *renamer) checkLabel(label *types.Label) {
399 // Check there are no identical labels in the function's label block.
400 // (Label blocks don't nest, so this is easy.)
401 if prev := label.Parent().Lookup(r.to); prev != nil {
402 r.errorf(label.Pos(), "renaming this label %q to %q", label.Name(), prev.Name())
403 r.errorf(prev.Pos(), "\twould conflict with this one")
407 // checkStructField checks that the field renaming will not cause
408 // conflicts at its declaration, or ambiguity or changes to any selection.
409 func (r *renamer) checkStructField(from *types.Var) {
410 // Check that the struct declaration is free of field conflicts,
411 // and field/method conflicts.
413 // go/types offers no easy way to get from a field (or interface
414 // method) to its declaring struct (or interface), so we must
416 info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
417 // path matches this pattern:
418 // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File]
420 // Ascend to FieldList.
423 if _, ok := path[i].(*ast.FieldList); ok {
429 tStruct := path[i].(*ast.StructType)
431 // Ascend past parens (unlikely).
433 _, ok := path[i].(*ast.ParenExpr)
439 if spec, ok := path[i].(*ast.TypeSpec); ok {
440 // This struct is also a named type.
441 // We must check for direct (non-promoted) field/field
442 // and method/field conflicts.
443 named := info.Defs[spec.Name].Type()
444 prev, indices, _ := types.LookupFieldOrMethod(named, true, info.Pkg, r.to)
445 if len(indices) == 1 {
446 r.errorf(from.Pos(), "renaming this field %q to %q",
448 r.errorf(prev.Pos(), "\twould conflict with this %s",
450 return // skip checkSelections to avoid redundant errors
453 // This struct is not a named type.
454 // We need only check for direct (non-promoted) field/field conflicts.
455 T := info.Types[tStruct].Type.Underlying().(*types.Struct)
456 for i := 0; i < T.NumFields(); i++ {
457 if prev := T.Field(i); prev.Name() == r.to {
458 r.errorf(from.Pos(), "renaming this field %q to %q",
460 r.errorf(prev.Pos(), "\twould conflict with this field")
461 return // skip checkSelections to avoid redundant errors
466 // Renaming an anonymous field requires renaming the type too. e.g.
467 // print(s.T) // if we rename T to U,
468 // type T int // this and
469 // var s struct {T} // this must change too.
470 if from.Anonymous() {
471 if named, ok := from.Type().(*types.Named); ok {
473 } else if named, ok := deref(from.Type()).(*types.Named); ok {
478 // Check integrity of existing (field and method) selections.
479 r.checkSelections(from)
482 // checkSelection checks that all uses and selections that resolve to
483 // the specified object would continue to do so after the renaming.
484 func (r *renamer) checkSelections(from types.Object) {
485 for pkg, info := range r.packages {
486 if id := someUse(info, from); id != nil {
487 if !r.checkExport(id, pkg, from) {
492 for syntax, sel := range info.Selections {
493 // There may be extant selections of only the old
494 // name or only the new name, so we must check both.
495 // (If neither, the renaming is sound.)
497 // In both cases, we wish to compare the lengths
498 // of the implicit field path (Selection.Index)
499 // to see if the renaming would change it.
501 // If a selection that resolves to 'from', when renamed,
502 // would yield a path of the same or shorter length,
503 // this indicates ambiguity or a changed referent,
504 // analogous to same- or sub-block lexical conflict.
506 // If a selection using the name 'to' would
507 // yield a path of the same or shorter length,
508 // this indicates ambiguity or shadowing,
509 // analogous to same- or super-block lexical conflict.
511 // TODO(adonovan): fix: derive from Types[syntax.X].Mode
512 // TODO(adonovan): test with pointer, value, addressable value.
513 isAddressable := true
515 if sel.Obj() == from {
516 if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil {
517 // Renaming this existing selection of
518 // 'from' may block access to an existing
519 // type member named 'to'.
520 delta := len(indices) - len(sel.Index())
522 continue // no ambiguity
524 r.selectionConflict(from, delta, syntax, obj)
528 } else if sel.Obj().Name() == r.to {
529 if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from {
530 // Renaming 'from' may cause this existing
531 // selection of the name 'to' to change
533 delta := len(indices) - len(sel.Index())
535 continue // no ambiguity
537 r.selectionConflict(from, -delta, syntax, sel.Obj())
545 func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) {
546 r.errorf(from.Pos(), "renaming this %s %q to %q",
547 objectKind(from), from.Name(), r.to)
551 // analogous to sub-block conflict
552 r.errorf(syntax.Sel.Pos(),
553 "\twould change the referent of this selection")
554 r.errorf(obj.Pos(), "\tof this %s", objectKind(obj))
556 // analogous to same-block conflict
557 r.errorf(syntax.Sel.Pos(),
558 "\twould make this reference ambiguous")
559 r.errorf(obj.Pos(), "\twith this %s", objectKind(obj))
561 // analogous to super-block conflict
562 r.errorf(syntax.Sel.Pos(),
563 "\twould shadow this selection")
564 r.errorf(obj.Pos(), "\tof the %s declared here",
569 // checkMethod performs safety checks for renaming a method.
570 // There are three hazards:
571 // - declaration conflicts
572 // - selection ambiguity/changes
573 // - entailed renamings of assignable concrete/interface types.
574 // We reject renamings initiated at concrete methods if it would
575 // change the assignability relation. For renamings of abstract
576 // methods, we rename all methods transitively coupled to it via
578 func (r *renamer) checkMethod(from *types.Func) {
580 if from.Pkg() == nil {
581 r.errorf(from.Pos(), "you cannot rename built-in method %s", from)
585 // ASSIGNABILITY: We reject renamings of concrete methods that
586 // would break a 'satisfy' constraint; but renamings of abstract
587 // methods are allowed to proceed, and we rename affected
588 // concrete and abstract methods as necessary. It is the
589 // initial method that determines the policy.
591 // Check for conflict at point of declaration.
592 // Check to ensure preservation of assignability requirements.
593 R := recv(from).Type()
598 prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to)
600 r.errorf(from.Pos(), "renaming this interface method %q to %q",
602 r.errorf(prev.Pos(), "\twould conflict with this method")
606 // Check all interfaces that embed this one for
607 // declaration conflicts too.
608 for _, info := range r.packages {
609 // Start with named interface types (better errors)
610 for _, obj := range info.Defs {
611 if obj, ok := obj.(*types.TypeName); ok && isInterface(obj.Type()) {
612 f, _, _ := types.LookupFieldOrMethod(
613 obj.Type(), false, from.Pkg(), from.Name())
617 t, _, _ := types.LookupFieldOrMethod(
618 obj.Type(), false, from.Pkg(), r.to)
622 r.errorf(from.Pos(), "renaming this interface method %q to %q",
624 r.errorf(t.Pos(), "\twould conflict with this method")
625 r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name())
629 // Now look at all literal interface types (includes named ones again).
630 for e, tv := range info.Types {
631 if e, ok := e.(*ast.InterfaceType); ok {
633 _ = tv.Type.(*types.Interface)
634 // TODO(adonovan): implement same check as above.
641 // Find the set of concrete or abstract methods directly
642 // coupled to abstract method 'from' by some
643 // satisfy.Constraint, and rename them too.
644 for key := range r.satisfy() {
645 // key = (lhs, rhs) where lhs is always an interface.
647 lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
651 rmethods := r.msets.MethodSet(key.RHS)
652 rsel := rmethods.Lookup(from.Pkg(), from.Name())
657 // If both sides have a method of this name,
658 // and one of them is m, the other must be coupled.
659 var coupled *types.Func
662 coupled = rsel.Obj().(*types.Func)
664 coupled = lsel.Obj().(*types.Func)
669 // We must treat concrete-to-interface
670 // constraints like an implicit selection C.f of
671 // each interface method I.f, and check that the
672 // renaming leaves the selection unchanged and
675 // Fun fact: the implicit selection of C.f
676 // type I interface{f()}
679 // var _ I = C{} // here
680 // yields abstract method I.f. This can make error
681 // messages less than obvious.
683 if !isInterface(key.RHS) {
684 // The logic below was derived from checkSelections.
686 rtosel := rmethods.Lookup(from.Pkg(), r.to)
688 rto := rtosel.Obj().(*types.Func)
689 delta := len(rsel.Index()) - len(rtosel.Index())
691 continue // no ambiguity
694 // TODO(adonovan): record the constraint's position.
695 keyPos := token.NoPos
697 r.errorf(from.Pos(), "renaming this method %q to %q",
700 // analogous to same-block conflict
701 r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous",
702 r.to, key.RHS, key.LHS)
703 r.errorf(rto.Pos(), "\twith (%s).%s",
704 recv(rto).Type(), r.to)
706 // analogous to super-block conflict
707 r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s",
708 r.to, key.RHS, key.LHS)
709 r.errorf(coupled.Pos(), "\tfrom (%s).%s",
710 recv(coupled).Type(), r.to)
711 r.errorf(rto.Pos(), "\tto (%s).%s",
712 recv(rto).Type(), r.to)
714 return // one error is enough
718 if !r.changeMethods {
719 // This should be unreachable.
720 r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from)
721 r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled)
722 r.errorf(from.Pos(), "\tPlease file a bug report")
726 // Rename the coupled method to preserve assignability.
733 prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), r.to)
734 if prev != nil && len(indices) == 1 {
735 r.errorf(from.Pos(), "renaming this method %q to %q",
737 r.errorf(prev.Pos(), "\twould conflict with this %s",
744 // Find the set of abstract methods coupled to concrete
745 // method 'from' by some satisfy.Constraint, and rename
748 // Coupling may be indirect, e.g. I.f <-> C.f via type D.
750 // type I interface {f()}
756 for key := range r.satisfy() {
757 // key = (lhs, rhs) where lhs is always an interface.
758 if isInterface(key.RHS) {
761 rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name())
762 if rsel == nil || rsel.Obj() != from {
763 continue // rhs does not have the method
765 lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
769 imeth := lsel.Obj().(*types.Func)
771 // imeth is the abstract method (e.g. I.f)
772 // and key.RHS is the concrete coupling type (e.g. D).
773 if !r.changeMethods {
774 r.errorf(from.Pos(), "renaming this method %q to %q",
779 I := recv(imeth).Type()
780 if named, ok := I.(*types.Named); ok {
781 pos = named.Obj().Pos()
782 iface = "interface " + named.Obj().Name()
787 r.errorf(pos, "\twould make %s no longer assignable to %s",
789 r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)",
791 return // one error is enough
794 // Rename the coupled interface method to preserve assignability.
799 // Check integrity of existing (field and method) selections.
800 // We skip this if there were errors above, to avoid redundant errors.
801 r.checkSelections(from)
804 func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool {
805 // Reject cross-package references if r.to is unexported.
806 // (Such references may be qualified identifiers or field/method
808 if !ast.IsExported(r.to) && pkg != from.Pkg() {
810 "renaming this %s %q to %q would make it unexported",
811 objectKind(from), from.Name(), r.to)
812 r.errorf(id.Pos(), "\tbreaking references from packages such as %q",
819 // satisfy returns the set of interface satisfaction constraints.
820 func (r *renamer) satisfy() map[satisfy.Constraint]bool {
821 if r.satisfyConstraints == nil {
822 // Compute on demand: it's expensive.
824 for _, info := range r.packages {
825 f.Find(&info.Info, info.Files)
827 r.satisfyConstraints = f.Result
829 return r.satisfyConstraints
832 // -- helpers ----------------------------------------------------------
834 // recv returns the method's receiver.
835 func recv(meth *types.Func) *types.Var {
836 return meth.Type().(*types.Signature).Recv()
839 // someUse returns an arbitrary use of obj within info.
840 func someUse(info *loader.PackageInfo, obj types.Object) *ast.Ident {
841 for id, o := range info.Uses {
849 // -- Plundered from golang.org/x/tools/go/ssa -----------------
851 func isInterface(T types.Type) bool { return types.IsInterface(T) }
853 func deref(typ types.Type) types.Type {
854 if p, _ := typ.(*types.Pointer); p != nil {