github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/refactor/rename/check.go (about) 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. 4 5 package rename 6 7 // This file defines the safety checks for each kind of renaming. 8 9 import ( 10 "fmt" 11 "go/ast" 12 "go/token" 13 "go/types" 14 15 "golang.org/x/tools/go/loader" 16 "golang.org/x/tools/refactor/satisfy" 17 ) 18 19 // errorf reports an error (e.g. conflict) and prevents file modification. 20 func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) { 21 r.hadConflicts = true 22 reportError(r.iprog.Fset.Position(pos), fmt.Sprintf(format, args...)) 23 } 24 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] { 28 return 29 } 30 r.objsToUpdate[from] = true 31 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 { 36 r.checkLabel(from_) 37 } else if isPackageLevel(from) { 38 r.checkInPackageBlock(from) 39 } else if v, ok := from.(*types.Var); ok && v.IsField() { 40 r.checkStructField(v) 41 } else if f, ok := from.(*types.Func); ok && recv(f) != nil { 42 r.checkMethod(f) 43 } else if isLocal(from) { 44 r.checkInLocalScope(from) 45 } else { 46 r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n", 47 objectKind(from), from) 48 } 49 } 50 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". 55 if r.to == "init" { 56 r.errorf(from.Pos(), "%q is not a valid imported package name", r.to) 57 } 58 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", 64 objectKind(prev)) 65 return // since checkInPackageBlock would report redundant errors 66 } 67 68 // Check for conflicts in lexical scope. 69 r.checkInLexicalScope(from, r.packages[from.Pkg()]) 70 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 76 } else { 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 81 } 82 } 83 } 84 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() { 93 continue 94 } 95 if id := someUse(info, from); id != nil && 96 !r.checkExport(id, pkg, from) { 97 break 98 } 99 } 100 } 101 102 info := r.packages[from.Pkg()] 103 104 // Check that in the package block, "init" is a function, and never referenced. 105 if r.to == "init" { 106 kind := objectKind(from) 107 if kind == "func" { 108 // Reject if intra-package references to it exist. 109 for id, obj := range info.Uses { 110 if obj == from { 111 r.errorf(from.Pos(), 112 "renaming this func %q to %q would make it a package initializer", 113 from.Name(), r.to) 114 r.errorf(id.Pos(), "\tbut references to it exist") 115 break 116 } 117 } 118 } else { 119 r.errorf(from.Pos(), "you cannot have a %s at package level named %q", 120 kind, r.to) 121 } 122 } 123 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) 128 if b == fileScope { 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", 132 objectKind(prev)) 133 return // since checkInPackageBlock would report redundant errors 134 } 135 } 136 137 // Check for conflicts in lexical scope. 138 if from.Exported() { 139 for _, info := range r.packages { 140 r.checkInLexicalScope(from, info) 141 } 142 } else { 143 r.checkInLexicalScope(from, info) 144 } 145 } 146 147 func (r *renamer) checkInLocalScope(from types.Object) { 148 info := r.packages[from.Pkg()] 149 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. 153 // 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) 157 // } 158 // 159 var isCaseVar bool 160 for syntax, obj := range info.Implicits { 161 if _, ok := syntax.(*ast.CaseClause); ok && obj.Pos() == from.Pos() { 162 isCaseVar = true 163 r.check(obj) 164 } 165 } 166 167 r.checkInLexicalScope(from, info) 168 169 // Finally, if this was a type switch, change the variable y. 170 if isCaseVar { 171 _, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos()) 172 path[0].(*ast.Ident).Name = r.to // path is [Ident AssignStmt TypeSwitchStmt...] 173 } 174 } 175 176 // checkInLexicalScope performs safety checks that a renaming does not 177 // change the lexical reference structure of the specified package. 178 // 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: 182 // 183 // var x int 184 // var z int 185 // 186 // func f(y int) { 187 // print(x) 188 // print(y) 189 // } 190 // 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. 194 // 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. 199 // 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 205 // the super-block. 206 // 207 // Removing the old name (and all references to it) is always safe, and 208 // requires no checks. 209 func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) { 210 b := from.Parent() // the block defining the 'from' object 211 if b != nil { 212 toBlock, to := b.LookupParent(r.to, from.Parent().End()) 213 if toBlock == b { 214 // same-block conflict 215 r.errorf(from.Pos(), "renaming this %s %q to %q", 216 objectKind(from), from.Name(), r.to) 217 r.errorf(to.Pos(), "\tconflicts with %s in same block", 218 objectKind(to)) 219 return 220 } else if toBlock != nil { 221 // Check for super-block conflict. 222 // The name r.to is defined in a superblock. 223 // Is that name referenced from within this block? 224 forEachLexicalRef(info, to, func(id *ast.Ident, block *types.Scope) bool { 225 _, obj := lexicalLookup(block, from.Name(), id.Pos()) 226 if obj == from { 227 // super-block conflict 228 r.errorf(from.Pos(), "renaming this %s %q to %q", 229 objectKind(from), from.Name(), r.to) 230 r.errorf(id.Pos(), "\twould shadow this reference") 231 r.errorf(to.Pos(), "\tto the %s declared here", 232 objectKind(to)) 233 return false // stop 234 } 235 return true 236 }) 237 } 238 } 239 240 // Check for sub-block conflict. 241 // Is there an intervening definition of r.to between 242 // the block defining 'from' and some reference to it? 243 forEachLexicalRef(info, from, func(id *ast.Ident, block *types.Scope) bool { 244 // Find the block that defines the found reference. 245 // It may be an ancestor. 246 fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos()) 247 248 // See what r.to would resolve to in the same scope. 249 toBlock, to := lexicalLookup(block, r.to, id.Pos()) 250 if to != nil { 251 // sub-block conflict 252 if deeper(toBlock, fromBlock) { 253 r.errorf(from.Pos(), "renaming this %s %q to %q", 254 objectKind(from), from.Name(), r.to) 255 r.errorf(id.Pos(), "\twould cause this reference to become shadowed") 256 r.errorf(to.Pos(), "\tby this intervening %s definition", 257 objectKind(to)) 258 return false // stop 259 } 260 } 261 return true 262 }) 263 264 // Renaming a type that is used as an embedded field 265 // requires renaming the field too. e.g. 266 // type T int // if we rename this to U.. 267 // var s struct {T} 268 // print(s.T) // ...this must change too 269 if _, ok := from.(*types.TypeName); ok { 270 for id, obj := range info.Uses { 271 if obj == from { 272 if field := info.Defs[id]; field != nil { 273 r.check(field) 274 } 275 } 276 } 277 } 278 } 279 280 // lexicalLookup is like (*types.Scope).LookupParent but respects the 281 // environment visible at pos. It assumes the relative position 282 // information is correct with each file. 283 func lexicalLookup(block *types.Scope, name string, pos token.Pos) (*types.Scope, types.Object) { 284 for b := block; b != nil; b = b.Parent() { 285 obj := b.Lookup(name) 286 // The scope of a package-level object is the entire package, 287 // so ignore pos in that case. 288 // No analogous clause is needed for file-level objects 289 // since no reference can appear before an import decl. 290 if obj != nil && (b == obj.Pkg().Scope() || obj.Pos() < pos) { 291 return b, obj 292 } 293 } 294 return nil, nil 295 } 296 297 // deeper reports whether block x is lexically deeper than y. 298 func deeper(x, y *types.Scope) bool { 299 if x == y || x == nil { 300 return false 301 } else if y == nil { 302 return true 303 } else { 304 return deeper(x.Parent(), y.Parent()) 305 } 306 } 307 308 // forEachLexicalRef calls fn(id, block) for each identifier id in package 309 // info that is a reference to obj in lexical scope. block is the 310 // lexical block enclosing the reference. If fn returns false the 311 // iteration is terminated and findLexicalRefs returns false. 312 func forEachLexicalRef(info *loader.PackageInfo, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool { 313 ok := true 314 var stack []ast.Node 315 316 var visit func(n ast.Node) bool 317 visit = func(n ast.Node) bool { 318 if n == nil { 319 stack = stack[:len(stack)-1] // pop 320 return false 321 } 322 if !ok { 323 return false // bail out 324 } 325 326 stack = append(stack, n) // push 327 switch n := n.(type) { 328 case *ast.Ident: 329 if info.Uses[n] == obj { 330 block := enclosingBlock(&info.Info, stack) 331 if !fn(n, block) { 332 ok = false 333 } 334 } 335 return visit(nil) // pop stack 336 337 case *ast.SelectorExpr: 338 // don't visit n.Sel 339 ast.Inspect(n.X, visit) 340 return visit(nil) // pop stack, don't descend 341 342 case *ast.CompositeLit: 343 // Handle recursion ourselves for struct literals 344 // so we don't visit field identifiers. 345 tv := info.Types[n] 346 if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok { 347 if n.Type != nil { 348 ast.Inspect(n.Type, visit) 349 } 350 for _, elt := range n.Elts { 351 if kv, ok := elt.(*ast.KeyValueExpr); ok { 352 ast.Inspect(kv.Value, visit) 353 } else { 354 ast.Inspect(elt, visit) 355 } 356 } 357 return visit(nil) // pop stack, don't descend 358 } 359 } 360 return true 361 } 362 363 for _, f := range info.Files { 364 ast.Inspect(f, visit) 365 if len(stack) != 0 { 366 panic(stack) 367 } 368 if !ok { 369 break 370 } 371 } 372 return ok 373 } 374 375 // enclosingBlock returns the innermost block enclosing the specified 376 // AST node, specified in the form of a path from the root of the file, 377 // [file...n]. 378 func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope { 379 for i := range stack { 380 n := stack[len(stack)-1-i] 381 // For some reason, go/types always associates a 382 // function's scope with its FuncType. 383 // TODO(adonovan): feature or a bug? 384 switch f := n.(type) { 385 case *ast.FuncDecl: 386 n = f.Type 387 case *ast.FuncLit: 388 n = f.Type 389 } 390 if b := info.Scopes[n]; b != nil { 391 return b 392 } 393 } 394 panic("no Scope for *ast.File") 395 } 396 397 func (r *renamer) checkLabel(label *types.Label) { 398 // Check there are no identical labels in the function's label block. 399 // (Label blocks don't nest, so this is easy.) 400 if prev := label.Parent().Lookup(r.to); prev != nil { 401 r.errorf(label.Pos(), "renaming this label %q to %q", label.Name(), prev.Name()) 402 r.errorf(prev.Pos(), "\twould conflict with this one") 403 } 404 } 405 406 // checkStructField checks that the field renaming will not cause 407 // conflicts at its declaration, or ambiguity or changes to any selection. 408 func (r *renamer) checkStructField(from *types.Var) { 409 // Check that the struct declaration is free of field conflicts, 410 // and field/method conflicts. 411 412 // go/types offers no easy way to get from a field (or interface 413 // method) to its declaring struct (or interface), so we must 414 // ascend the AST. 415 info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos()) 416 // path matches this pattern: 417 // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File] 418 419 // Ascend to FieldList. 420 var i int 421 for { 422 if _, ok := path[i].(*ast.FieldList); ok { 423 break 424 } 425 i++ 426 } 427 i++ 428 tStruct := path[i].(*ast.StructType) 429 i++ 430 // Ascend past parens (unlikely). 431 for { 432 _, ok := path[i].(*ast.ParenExpr) 433 if !ok { 434 break 435 } 436 i++ 437 } 438 if spec, ok := path[i].(*ast.TypeSpec); ok { 439 // This struct is also a named type. 440 // We must check for direct (non-promoted) field/field 441 // and method/field conflicts. 442 named := info.Defs[spec.Name].Type() 443 prev, indices, _ := types.LookupFieldOrMethod(named, true, info.Pkg, r.to) 444 if len(indices) == 1 { 445 r.errorf(from.Pos(), "renaming this field %q to %q", 446 from.Name(), r.to) 447 r.errorf(prev.Pos(), "\twould conflict with this %s", 448 objectKind(prev)) 449 return // skip checkSelections to avoid redundant errors 450 } 451 } else { 452 // This struct is not a named type. 453 // We need only check for direct (non-promoted) field/field conflicts. 454 T := info.Types[tStruct].Type.Underlying().(*types.Struct) 455 for i := 0; i < T.NumFields(); i++ { 456 if prev := T.Field(i); prev.Name() == r.to { 457 r.errorf(from.Pos(), "renaming this field %q to %q", 458 from.Name(), r.to) 459 r.errorf(prev.Pos(), "\twould conflict with this field") 460 return // skip checkSelections to avoid redundant errors 461 } 462 } 463 } 464 465 // Renaming an anonymous field requires renaming the type too. e.g. 466 // print(s.T) // if we rename T to U, 467 // type T int // this and 468 // var s struct {T} // this must change too. 469 if from.Anonymous() { 470 if named, ok := from.Type().(*types.Named); ok { 471 r.check(named.Obj()) 472 } else if named, ok := deref(from.Type()).(*types.Named); ok { 473 r.check(named.Obj()) 474 } 475 } 476 477 // Check integrity of existing (field and method) selections. 478 r.checkSelections(from) 479 } 480 481 // checkSelections checks that all uses and selections that resolve to 482 // the specified object would continue to do so after the renaming. 483 func (r *renamer) checkSelections(from types.Object) { 484 for pkg, info := range r.packages { 485 if id := someUse(info, from); id != nil { 486 if !r.checkExport(id, pkg, from) { 487 return 488 } 489 } 490 491 for syntax, sel := range info.Selections { 492 // There may be extant selections of only the old 493 // name or only the new name, so we must check both. 494 // (If neither, the renaming is sound.) 495 // 496 // In both cases, we wish to compare the lengths 497 // of the implicit field path (Selection.Index) 498 // to see if the renaming would change it. 499 // 500 // If a selection that resolves to 'from', when renamed, 501 // would yield a path of the same or shorter length, 502 // this indicates ambiguity or a changed referent, 503 // analogous to same- or sub-block lexical conflict. 504 // 505 // If a selection using the name 'to' would 506 // yield a path of the same or shorter length, 507 // this indicates ambiguity or shadowing, 508 // analogous to same- or super-block lexical conflict. 509 510 // TODO(adonovan): fix: derive from Types[syntax.X].Mode 511 // TODO(adonovan): test with pointer, value, addressable value. 512 isAddressable := true 513 514 if sel.Obj() == from { 515 if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil { 516 // Renaming this existing selection of 517 // 'from' may block access to an existing 518 // type member named 'to'. 519 delta := len(indices) - len(sel.Index()) 520 if delta > 0 { 521 continue // no ambiguity 522 } 523 r.selectionConflict(from, delta, syntax, obj) 524 return 525 } 526 527 } else if sel.Obj().Name() == r.to { 528 if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from { 529 // Renaming 'from' may cause this existing 530 // selection of the name 'to' to change 531 // its meaning. 532 delta := len(indices) - len(sel.Index()) 533 if delta > 0 { 534 continue // no ambiguity 535 } 536 r.selectionConflict(from, -delta, syntax, sel.Obj()) 537 return 538 } 539 } 540 } 541 } 542 } 543 544 func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) { 545 r.errorf(from.Pos(), "renaming this %s %q to %q", 546 objectKind(from), from.Name(), r.to) 547 548 switch { 549 case delta < 0: 550 // analogous to sub-block conflict 551 r.errorf(syntax.Sel.Pos(), 552 "\twould change the referent of this selection") 553 r.errorf(obj.Pos(), "\tof this %s", objectKind(obj)) 554 case delta == 0: 555 // analogous to same-block conflict 556 r.errorf(syntax.Sel.Pos(), 557 "\twould make this reference ambiguous") 558 r.errorf(obj.Pos(), "\twith this %s", objectKind(obj)) 559 case delta > 0: 560 // analogous to super-block conflict 561 r.errorf(syntax.Sel.Pos(), 562 "\twould shadow this selection") 563 r.errorf(obj.Pos(), "\tof the %s declared here", 564 objectKind(obj)) 565 } 566 } 567 568 // checkMethod performs safety checks for renaming a method. 569 // There are three hazards: 570 // - declaration conflicts 571 // - selection ambiguity/changes 572 // - entailed renamings of assignable concrete/interface types. 573 // 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 577 // assignability. 578 func (r *renamer) checkMethod(from *types.Func) { 579 // e.g. error.Error 580 if from.Pkg() == nil { 581 r.errorf(from.Pos(), "you cannot rename built-in method %s", from) 582 return 583 } 584 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. 590 591 // Check for conflict at point of declaration. 592 // Check to ensure preservation of assignability requirements. 593 R := recv(from).Type() 594 if isInterface(R) { 595 // Abstract method 596 597 // declaration 598 prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to) 599 if prev != nil { 600 r.errorf(from.Pos(), "renaming this interface method %q to %q", 601 from.Name(), r.to) 602 r.errorf(prev.Pos(), "\twould conflict with this method") 603 return 604 } 605 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()) 614 if f == nil { 615 continue 616 } 617 t, _, _ := types.LookupFieldOrMethod( 618 obj.Type(), false, from.Pkg(), r.to) 619 if t == nil { 620 continue 621 } 622 r.errorf(from.Pos(), "renaming this interface method %q to %q", 623 from.Name(), r.to) 624 r.errorf(t.Pos(), "\twould conflict with this method") 625 r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name()) 626 } 627 } 628 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 { 632 _ = e 633 _ = tv.Type.(*types.Interface) 634 // TODO(adonovan): implement same check as above. 635 } 636 } 637 } 638 639 // assignability 640 // 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. 646 647 lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) 648 if lsel == nil { 649 continue 650 } 651 rmethods := r.msets.MethodSet(key.RHS) 652 rsel := rmethods.Lookup(from.Pkg(), from.Name()) 653 if rsel == nil { 654 continue 655 } 656 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 660 switch from { 661 case lsel.Obj(): 662 coupled = rsel.Obj().(*types.Func) 663 case rsel.Obj(): 664 coupled = lsel.Obj().(*types.Func) 665 default: 666 continue 667 } 668 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 673 // unambiguous. 674 // 675 // Fun fact: the implicit selection of C.f 676 // type I interface{f()} 677 // type C struct{I} 678 // func (C) g() 679 // var _ I = C{} // here 680 // yields abstract method I.f. This can make error 681 // messages less than obvious. 682 // 683 if !isInterface(key.RHS) { 684 // The logic below was derived from checkSelections. 685 686 rtosel := rmethods.Lookup(from.Pkg(), r.to) 687 if rtosel != nil { 688 rto := rtosel.Obj().(*types.Func) 689 delta := len(rsel.Index()) - len(rtosel.Index()) 690 if delta < 0 { 691 continue // no ambiguity 692 } 693 694 // TODO(adonovan): record the constraint's position. 695 keyPos := token.NoPos 696 697 r.errorf(from.Pos(), "renaming this method %q to %q", 698 from.Name(), r.to) 699 if delta == 0 { 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) 705 } else { 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) 713 } 714 return // one error is enough 715 } 716 } 717 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") 723 return 724 } 725 726 // Rename the coupled method to preserve assignability. 727 r.check(coupled) 728 } 729 } else { 730 // Concrete method 731 732 // declaration 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", 736 from.Name(), r.to) 737 r.errorf(prev.Pos(), "\twould conflict with this %s", 738 objectKind(prev)) 739 return 740 } 741 742 // assignability 743 // 744 // Find the set of abstract methods coupled to concrete 745 // method 'from' by some satisfy.Constraint, and rename 746 // them too. 747 // 748 // Coupling may be indirect, e.g. I.f <-> C.f via type D. 749 // 750 // type I interface {f()} 751 // type C int 752 // type (C) f() 753 // type D struct{C} 754 // var _ I = D{} 755 // 756 for key := range r.satisfy() { 757 // key = (lhs, rhs) where lhs is always an interface. 758 if isInterface(key.RHS) { 759 continue 760 } 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 764 } 765 lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) 766 if lsel == nil { 767 continue 768 } 769 imeth := lsel.Obj().(*types.Func) 770 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", 775 from.Name(), r.to) 776 var pos token.Pos 777 var iface string 778 779 I := recv(imeth).Type() 780 if named, ok := I.(*types.Named); ok { 781 pos = named.Obj().Pos() 782 iface = "interface " + named.Obj().Name() 783 } else { 784 pos = from.Pos() 785 iface = I.String() 786 } 787 r.errorf(pos, "\twould make %s no longer assignable to %s", 788 key.RHS, iface) 789 r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)", 790 I, from.Name()) 791 return // one error is enough 792 } 793 794 // Rename the coupled interface method to preserve assignability. 795 r.check(imeth) 796 } 797 } 798 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) 802 } 803 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 807 // selections.) 808 if !ast.IsExported(r.to) && pkg != from.Pkg() { 809 r.errorf(from.Pos(), 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", 813 pkg.Path()) 814 return false 815 } 816 return true 817 } 818 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. 823 var f satisfy.Finder 824 for _, info := range r.packages { 825 f.Find(&info.Info, info.Files) 826 } 827 r.satisfyConstraints = f.Result 828 } 829 return r.satisfyConstraints 830 } 831 832 // -- helpers ---------------------------------------------------------- 833 834 // recv returns the method's receiver. 835 func recv(meth *types.Func) *types.Var { 836 return meth.Type().(*types.Signature).Recv() 837 } 838 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 { 842 if o == obj { 843 return id 844 } 845 } 846 return nil 847 } 848 849 // -- Plundered from golang.org/x/tools/go/ssa ----------------- 850 851 func isInterface(T types.Type) bool { return types.IsInterface(T) } 852 853 func deref(typ types.Type) types.Type { 854 if p, _ := typ.(*types.Pointer); p != nil { 855 return p.Elem() 856 } 857 return typ 858 }