golang.org/x/tools/gopls@v0.15.3/internal/golang/rename.go (about) 1 // Copyright 2019 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 golang 6 7 // TODO(adonovan): 8 // 9 // - method of generic concrete type -> arbitrary instances of same 10 // 11 // - make satisfy work across packages. 12 // 13 // - tests, tests, tests: 14 // - play with renamings in the k8s tree. 15 // - generics 16 // - error cases (e.g. conflicts) 17 // - renaming a symbol declared in the module cache 18 // (currently proceeds with half of the renaming!) 19 // - make sure all tests have both a local and a cross-package analogue. 20 // - look at coverage 21 // - special cases: embedded fields, interfaces, test variants, 22 // function-local things with uppercase names; 23 // packages with type errors (currently 'satisfy' rejects them), 24 // package with missing imports; 25 // 26 // - measure performance in k8s. 27 // 28 // - The original gorename tool assumed well-typedness, but the gopls feature 29 // does no such check (which actually makes it much more useful). 30 // Audit to ensure it is safe on ill-typed code. 31 // 32 // - Generics support was no doubt buggy before but incrementalization 33 // may have exacerbated it. If the problem were just about objects, 34 // defs and uses it would be fairly simple, but type assignability 35 // comes into play in the 'satisfy' check for method renamings. 36 // De-instantiating Vector[int] to Vector[T] changes its type. 37 // We need to come up with a theory for the satisfy check that 38 // works with generics, and across packages. We currently have no 39 // simple way to pass types between packages (think: objectpath for 40 // types), though presumably exportdata could be pressed into service. 41 // 42 // - FileID-based de-duplication of edits to different URIs for the same file. 43 44 import ( 45 "context" 46 "errors" 47 "fmt" 48 "go/ast" 49 "go/token" 50 "go/types" 51 "path" 52 "path/filepath" 53 "regexp" 54 "sort" 55 "strconv" 56 "strings" 57 58 "golang.org/x/mod/modfile" 59 "golang.org/x/tools/go/ast/astutil" 60 "golang.org/x/tools/go/types/objectpath" 61 "golang.org/x/tools/go/types/typeutil" 62 "golang.org/x/tools/gopls/internal/cache" 63 "golang.org/x/tools/gopls/internal/cache/metadata" 64 "golang.org/x/tools/gopls/internal/cache/parsego" 65 "golang.org/x/tools/gopls/internal/file" 66 "golang.org/x/tools/gopls/internal/protocol" 67 "golang.org/x/tools/gopls/internal/util/bug" 68 "golang.org/x/tools/gopls/internal/util/safetoken" 69 "golang.org/x/tools/internal/diff" 70 "golang.org/x/tools/internal/event" 71 "golang.org/x/tools/internal/typeparams" 72 "golang.org/x/tools/refactor/satisfy" 73 ) 74 75 // A renamer holds state of a single call to renameObj, which renames 76 // an object (or several coupled objects) within a single type-checked 77 // syntax package. 78 type renamer struct { 79 pkg *cache.Package // the syntax package in which the renaming is applied 80 objsToUpdate map[types.Object]bool // records progress of calls to check 81 conflicts []string 82 from, to string 83 satisfyConstraints map[satisfy.Constraint]bool 84 msets typeutil.MethodSetCache 85 changeMethods bool 86 } 87 88 // A PrepareItem holds the result of a "prepare rename" operation: 89 // the source range and value of a selected identifier. 90 type PrepareItem struct { 91 Range protocol.Range 92 Text string 93 } 94 95 // PrepareRename searches for a valid renaming at position pp. 96 // 97 // The returned usererr is intended to be displayed to the user to explain why 98 // the prepare fails. Probably we could eliminate the redundancy in returning 99 // two errors, but for now this is done defensively. 100 func PrepareRename(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position) (_ *PrepareItem, usererr, err error) { 101 ctx, done := event.Start(ctx, "golang.PrepareRename") 102 defer done() 103 104 // Is the cursor within the package name declaration? 105 if pgf, inPackageName, err := parsePackageNameDecl(ctx, snapshot, f, pp); err != nil { 106 return nil, err, err 107 } else if inPackageName { 108 item, err := prepareRenamePackageName(ctx, snapshot, pgf) 109 return item, err, err 110 } 111 112 // Ordinary (non-package) renaming. 113 // 114 // Type-check the current package, locate the reference at the position, 115 // validate the object, and report its name and range. 116 // 117 // TODO(adonovan): in all cases below, we return usererr=nil, 118 // which means we return (nil, nil) at the protocol 119 // layer. This seems like a bug, or at best an exploitation of 120 // knowledge of VSCode-specific behavior. Can we avoid that? 121 pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, f.URI()) 122 if err != nil { 123 return nil, nil, err 124 } 125 pos, err := pgf.PositionPos(pp) 126 if err != nil { 127 return nil, nil, err 128 } 129 targets, node, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos) 130 if err != nil { 131 return nil, nil, err 132 } 133 var obj types.Object 134 for obj = range targets { 135 break // pick one arbitrarily 136 } 137 if err := checkRenamable(obj); err != nil { 138 return nil, nil, err 139 } 140 rng, err := pgf.NodeRange(node) 141 if err != nil { 142 return nil, nil, err 143 } 144 if _, isImport := node.(*ast.ImportSpec); isImport { 145 // We're not really renaming the import path. 146 rng.End = rng.Start 147 } 148 return &PrepareItem{ 149 Range: rng, 150 Text: obj.Name(), 151 }, nil, nil 152 } 153 154 func prepareRenamePackageName(ctx context.Context, snapshot *cache.Snapshot, pgf *ParsedGoFile) (*PrepareItem, error) { 155 // Does the client support file renaming? 156 fileRenameSupported := false 157 for _, op := range snapshot.Options().SupportedResourceOperations { 158 if op == protocol.Rename { 159 fileRenameSupported = true 160 break 161 } 162 } 163 if !fileRenameSupported { 164 return nil, errors.New("can't rename package: LSP client does not support file renaming") 165 } 166 167 // Check validity of the metadata for the file's containing package. 168 meta, err := NarrowestMetadataForFile(ctx, snapshot, pgf.URI) 169 if err != nil { 170 return nil, err 171 } 172 if meta.Name == "main" { 173 return nil, fmt.Errorf("can't rename package \"main\"") 174 } 175 if strings.HasSuffix(string(meta.Name), "_test") { 176 return nil, fmt.Errorf("can't rename x_test packages") 177 } 178 if meta.Module == nil { 179 return nil, fmt.Errorf("can't rename package: missing module information for package %q", meta.PkgPath) 180 } 181 if meta.Module.Path == string(meta.PkgPath) { 182 return nil, fmt.Errorf("can't rename package: package path %q is the same as module path %q", meta.PkgPath, meta.Module.Path) 183 } 184 185 // Return the location of the package declaration. 186 rng, err := pgf.NodeRange(pgf.File.Name) 187 if err != nil { 188 return nil, err 189 } 190 return &PrepareItem{ 191 Range: rng, 192 Text: string(meta.Name), 193 }, nil 194 } 195 196 func checkRenamable(obj types.Object) error { 197 switch obj := obj.(type) { 198 case *types.Var: 199 if obj.Embedded() { 200 return fmt.Errorf("can't rename embedded fields: rename the type directly or name the field") 201 } 202 case *types.Builtin, *types.Nil: 203 return fmt.Errorf("%s is built in and cannot be renamed", obj.Name()) 204 } 205 if obj.Pkg() == nil || obj.Pkg().Path() == "unsafe" { 206 // e.g. error.Error, unsafe.Pointer 207 return fmt.Errorf("%s is built in and cannot be renamed", obj.Name()) 208 } 209 if obj.Name() == "_" { 210 return errors.New("can't rename \"_\"") 211 } 212 return nil 213 } 214 215 // Rename returns a map of TextEdits for each file modified when renaming a 216 // given identifier within a package and a boolean value of true for renaming 217 // package and false otherwise. 218 func Rename(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position, newName string) (map[protocol.DocumentURI][]protocol.TextEdit, bool, error) { 219 ctx, done := event.Start(ctx, "golang.Rename") 220 defer done() 221 222 if !isValidIdentifier(newName) { 223 return nil, false, fmt.Errorf("invalid identifier to rename: %q", newName) 224 } 225 226 // Cursor within package name declaration? 227 _, inPackageName, err := parsePackageNameDecl(ctx, snapshot, f, pp) 228 if err != nil { 229 return nil, false, err 230 } 231 232 var editMap map[protocol.DocumentURI][]diff.Edit 233 if inPackageName { 234 editMap, err = renamePackageName(ctx, snapshot, f, PackageName(newName)) 235 } else { 236 editMap, err = renameOrdinary(ctx, snapshot, f, pp, newName) 237 } 238 if err != nil { 239 return nil, false, err 240 } 241 242 // Convert edits to protocol form. 243 result := make(map[protocol.DocumentURI][]protocol.TextEdit) 244 for uri, edits := range editMap { 245 // Sort and de-duplicate edits. 246 // 247 // Overlapping edits may arise in local renamings (due 248 // to type switch implicits) and globals ones (due to 249 // processing multiple package variants). 250 // 251 // We assume renaming produces diffs that are all 252 // replacements (no adjacent insertions that might 253 // become reordered) and that are either identical or 254 // non-overlapping. 255 diff.SortEdits(edits) 256 filtered := edits[:0] 257 for i, edit := range edits { 258 if i == 0 || edit != filtered[len(filtered)-1] { 259 filtered = append(filtered, edit) 260 } 261 } 262 edits = filtered 263 264 // TODO(adonovan): the logic above handles repeat edits to the 265 // same file URI (e.g. as a member of package p and p_test) but 266 // is not sufficient to handle file-system level aliasing arising 267 // from symbolic or hard links. For that, we should use a 268 // robustio-FileID-keyed map. 269 // See https://go.dev/cl/457615 for example. 270 // This really occurs in practice, e.g. kubernetes has 271 // vendor/k8s.io/kubectl -> ../../staging/src/k8s.io/kubectl. 272 fh, err := snapshot.ReadFile(ctx, uri) 273 if err != nil { 274 return nil, false, err 275 } 276 data, err := fh.Content() 277 if err != nil { 278 return nil, false, err 279 } 280 m := protocol.NewMapper(uri, data) 281 protocolEdits, err := protocol.EditsFromDiffEdits(m, edits) 282 if err != nil { 283 return nil, false, err 284 } 285 result[uri] = protocolEdits 286 } 287 288 return result, inPackageName, nil 289 } 290 291 // renameOrdinary renames an ordinary (non-package) name throughout the workspace. 292 func renameOrdinary(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position, newName string) (map[protocol.DocumentURI][]diff.Edit, error) { 293 // Type-check the referring package and locate the object(s). 294 // 295 // Unlike NarrowestPackageForFile, this operation prefers the 296 // widest variant as, for non-exported identifiers, it is the 297 // only package we need. (In case you're wondering why 298 // 'references' doesn't also want the widest variant: it 299 // computes the union across all variants.) 300 var targets map[types.Object]ast.Node 301 var pkg *cache.Package 302 { 303 mps, err := snapshot.MetadataForFile(ctx, f.URI()) 304 if err != nil { 305 return nil, err 306 } 307 metadata.RemoveIntermediateTestVariants(&mps) 308 if len(mps) == 0 { 309 return nil, fmt.Errorf("no package metadata for file %s", f.URI()) 310 } 311 widest := mps[len(mps)-1] // widest variant may include _test.go files 312 pkgs, err := snapshot.TypeCheck(ctx, widest.ID) 313 if err != nil { 314 return nil, err 315 } 316 pkg = pkgs[0] 317 pgf, err := pkg.File(f.URI()) 318 if err != nil { 319 return nil, err // "can't happen" 320 } 321 pos, err := pgf.PositionPos(pp) 322 if err != nil { 323 return nil, err 324 } 325 objects, _, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos) 326 if err != nil { 327 return nil, err 328 } 329 targets = objects 330 } 331 332 // Pick a representative object arbitrarily. 333 // (All share the same name, pos, and kind.) 334 var obj types.Object 335 for obj = range targets { 336 break 337 } 338 if obj.Name() == newName { 339 return nil, fmt.Errorf("old and new names are the same: %s", newName) 340 } 341 if err := checkRenamable(obj); err != nil { 342 return nil, err 343 } 344 345 // Find objectpath, if object is exported ("" otherwise). 346 var declObjPath objectpath.Path 347 if obj.Exported() { 348 // objectpath.For requires the origin of a generic function or type, not an 349 // instantiation (a bug?). Unfortunately we can't call Func.Origin as this 350 // is not available in go/types@go1.18. So we take a scenic route. 351 // 352 // Note that unlike Funcs, TypeNames are always canonical (they are "left" 353 // of the type parameters, unlike methods). 354 switch obj.(type) { // avoid "obj :=" since cases reassign the var 355 case *types.TypeName: 356 if _, ok := obj.Type().(*types.TypeParam); ok { 357 // As with capitalized function parameters below, type parameters are 358 // local. 359 goto skipObjectPath 360 } 361 case *types.Func: 362 obj = funcOrigin(obj.(*types.Func)) 363 case *types.Var: 364 // TODO(adonovan): do vars need the origin treatment too? (issue #58462) 365 366 // Function parameter and result vars that are (unusually) 367 // capitalized are technically exported, even though they 368 // cannot be referenced, because they may affect downstream 369 // error messages. But we can safely treat them as local. 370 // 371 // This is not merely an optimization: the renameExported 372 // operation gets confused by such vars. It finds them from 373 // objectpath, the classifies them as local vars, but as 374 // they came from export data they lack syntax and the 375 // correct scope tree (issue #61294). 376 if !obj.(*types.Var).IsField() && !isPackageLevel(obj) { 377 goto skipObjectPath 378 } 379 } 380 if path, err := objectpath.For(obj); err == nil { 381 declObjPath = path 382 } 383 skipObjectPath: 384 } 385 386 // Nonexported? Search locally. 387 if declObjPath == "" { 388 var objects []types.Object 389 for obj := range targets { 390 objects = append(objects, obj) 391 } 392 editMap, _, err := renameObjects(newName, pkg, objects...) 393 return editMap, err 394 } 395 396 // Exported: search globally. 397 // 398 // For exported package-level var/const/func/type objects, the 399 // search scope is just the direct importers. 400 // 401 // For exported fields and methods, the scope is the 402 // transitive rdeps. (The exportedness of the field's struct 403 // or method's receiver is irrelevant.) 404 transitive := false 405 switch obj := obj.(type) { 406 case *types.TypeName: 407 // Renaming an exported package-level type 408 // requires us to inspect all transitive rdeps 409 // in the event that the type is embedded. 410 // 411 // TODO(adonovan): opt: this is conservative 412 // but inefficient. Instead, expand the scope 413 // of the search only if we actually encounter 414 // an embedding of the type, and only then to 415 // the rdeps of the embedding package. 416 if obj.Parent() == obj.Pkg().Scope() { 417 transitive = true 418 } 419 420 case *types.Var: 421 if obj.IsField() { 422 transitive = true // field 423 } 424 425 // TODO(adonovan): opt: process only packages that 426 // contain a reference (xrefs) to the target field. 427 428 case *types.Func: 429 if obj.Type().(*types.Signature).Recv() != nil { 430 transitive = true // method 431 } 432 433 // It's tempting to optimize by skipping 434 // packages that don't contain a reference to 435 // the method in the xrefs index, but we still 436 // need to apply the satisfy check to those 437 // packages to find assignment statements that 438 // might expands the scope of the renaming. 439 } 440 441 // Type-check all the packages to inspect. 442 declURI := protocol.URIFromPath(pkg.FileSet().File(obj.Pos()).Name()) 443 pkgs, err := typeCheckReverseDependencies(ctx, snapshot, declURI, transitive) 444 if err != nil { 445 return nil, err 446 } 447 448 // Apply the renaming to the (initial) object. 449 declPkgPath := PackagePath(obj.Pkg().Path()) 450 return renameExported(pkgs, declPkgPath, declObjPath, newName) 451 } 452 453 // funcOrigin is a go1.18-portable implementation of (*types.Func).Origin. 454 func funcOrigin(fn *types.Func) *types.Func { 455 // Method? 456 if fn.Type().(*types.Signature).Recv() != nil { 457 return typeparams.OriginMethod(fn) 458 } 459 460 // Package-level function? 461 // (Assume the origin has the same position.) 462 gen := fn.Pkg().Scope().Lookup(fn.Name()) 463 if gen != nil && gen.Pos() == fn.Pos() { 464 return gen.(*types.Func) 465 } 466 467 return fn 468 } 469 470 // typeCheckReverseDependencies returns the type-checked packages for 471 // the reverse dependencies of all packages variants containing 472 // file declURI. The packages are in some topological order. 473 // 474 // It includes all variants (even intermediate test variants) for the 475 // purposes of computing reverse dependencies, but discards ITVs for 476 // the actual renaming work. 477 // 478 // (This neglects obscure edge cases where a _test.go file changes the 479 // selectors used only in an ITV, but life is short. Also sin must be 480 // punished.) 481 func typeCheckReverseDependencies(ctx context.Context, snapshot *cache.Snapshot, declURI protocol.DocumentURI, transitive bool) ([]*cache.Package, error) { 482 variants, err := snapshot.MetadataForFile(ctx, declURI) 483 if err != nil { 484 return nil, err 485 } 486 // variants must include ITVs for the reverse dependency 487 // computation, but they are filtered out before we typecheck. 488 allRdeps := make(map[PackageID]*metadata.Package) 489 for _, variant := range variants { 490 rdeps, err := snapshot.ReverseDependencies(ctx, variant.ID, transitive) 491 if err != nil { 492 return nil, err 493 } 494 allRdeps[variant.ID] = variant // include self 495 for id, meta := range rdeps { 496 allRdeps[id] = meta 497 } 498 } 499 var ids []PackageID 500 for id, meta := range allRdeps { 501 if meta.IsIntermediateTestVariant() { 502 continue 503 } 504 ids = append(ids, id) 505 } 506 507 // Sort the packages into some topological order of the 508 // (unfiltered) metadata graph. 509 metadata.SortPostOrder(snapshot, ids) 510 511 // Dependencies must be visited first since they can expand 512 // the search set. Ideally we would process the (filtered) set 513 // of packages in the parallel postorder of the snapshot's 514 // (unfiltered) metadata graph, but this is quite tricky 515 // without a good graph abstraction. 516 // 517 // For now, we visit packages sequentially in order of 518 // ascending height, like an inverted breadth-first search. 519 // 520 // Type checking is by far the dominant cost, so 521 // overlapping it with renaming may not be worthwhile. 522 return snapshot.TypeCheck(ctx, ids...) 523 } 524 525 // renameExported renames the object denoted by (pkgPath, objPath) 526 // within the specified packages, along with any other objects that 527 // must be renamed as a consequence. The slice of packages must be 528 // topologically ordered. 529 func renameExported(pkgs []*cache.Package, declPkgPath PackagePath, declObjPath objectpath.Path, newName string) (map[protocol.DocumentURI][]diff.Edit, error) { 530 531 // A target is a name for an object that is stable across types.Packages. 532 type target struct { 533 pkg PackagePath 534 obj objectpath.Path 535 } 536 537 // Populate the initial set of target objects. 538 // This set may grow as we discover the consequences of each renaming. 539 // 540 // TODO(adonovan): strictly, each cone of reverse dependencies 541 // of a single variant should have its own target map that 542 // monotonically expands as we go up the import graph, because 543 // declarations in test files can alter the set of 544 // package-level names and change the meaning of field and 545 // method selectors. So if we parallelize the graph 546 // visitation (see above), we should also compute the targets 547 // as a union of dependencies. 548 // 549 // Or we could decide that the logic below is fast enough not 550 // to need parallelism. In small measurements so far the 551 // type-checking step is about 95% and the renaming only 5%. 552 targets := map[target]bool{{declPkgPath, declObjPath}: true} 553 554 // Apply the renaming operation to each package. 555 allEdits := make(map[protocol.DocumentURI][]diff.Edit) 556 for _, pkg := range pkgs { 557 558 // Resolved target objects within package pkg. 559 var objects []types.Object 560 for t := range targets { 561 p := pkg.DependencyTypes(t.pkg) 562 if p == nil { 563 continue // indirect dependency of no consequence 564 } 565 obj, err := objectpath.Object(p, t.obj) 566 if err != nil { 567 // Possibly a method or an unexported type 568 // that is not reachable through export data? 569 // See https://github.com/golang/go/issues/60789. 570 // 571 // TODO(adonovan): it seems unsatisfactory that Object 572 // should return an error for a "valid" path. Perhaps 573 // we should define such paths as invalid and make 574 // objectpath.For compute reachability? 575 // Would that be a compatible change? 576 continue 577 } 578 objects = append(objects, obj) 579 } 580 if len(objects) == 0 { 581 continue // no targets of consequence to this package 582 } 583 584 // Apply the renaming. 585 editMap, moreObjects, err := renameObjects(newName, pkg, objects...) 586 if err != nil { 587 return nil, err 588 } 589 590 // It is safe to concatenate the edits as they are non-overlapping 591 // (or identical, in which case they will be de-duped by Rename). 592 for uri, edits := range editMap { 593 allEdits[uri] = append(allEdits[uri], edits...) 594 } 595 596 // Expand the search set? 597 for obj := range moreObjects { 598 objpath, err := objectpath.For(obj) 599 if err != nil { 600 continue // not exported 601 } 602 target := target{PackagePath(obj.Pkg().Path()), objpath} 603 targets[target] = true 604 605 // TODO(adonovan): methods requires dynamic 606 // programming of the product targets x 607 // packages as any package might add a new 608 // target (from a foward dep) as a 609 // consequence, and any target might imply a 610 // new set of rdeps. See golang/go#58461. 611 } 612 } 613 614 return allEdits, nil 615 } 616 617 // renamePackageName renames package declarations, imports, and go.mod files. 618 func renamePackageName(ctx context.Context, s *cache.Snapshot, f file.Handle, newName PackageName) (map[protocol.DocumentURI][]diff.Edit, error) { 619 // Rename the package decl and all imports. 620 renamingEdits, err := renamePackage(ctx, s, f, newName) 621 if err != nil { 622 return nil, err 623 } 624 625 // Update the last component of the file's enclosing directory. 626 oldBase := filepath.Dir(f.URI().Path()) 627 newPkgDir := filepath.Join(filepath.Dir(oldBase), string(newName)) 628 629 // Update any affected replace directives in go.mod files. 630 // TODO(adonovan): extract into its own function. 631 // 632 // Get all workspace modules. 633 // TODO(adonovan): should this operate on all go.mod files, 634 // irrespective of whether they are included in the workspace? 635 modFiles := s.View().ModFiles() 636 for _, m := range modFiles { 637 fh, err := s.ReadFile(ctx, m) 638 if err != nil { 639 return nil, err 640 } 641 pm, err := s.ParseMod(ctx, fh) 642 if err != nil { 643 return nil, err 644 } 645 646 modFileDir := filepath.Dir(pm.URI.Path()) 647 affectedReplaces := []*modfile.Replace{} 648 649 // Check if any replace directives need to be fixed 650 for _, r := range pm.File.Replace { 651 if !strings.HasPrefix(r.New.Path, "/") && !strings.HasPrefix(r.New.Path, "./") && !strings.HasPrefix(r.New.Path, "../") { 652 continue 653 } 654 655 replacedPath := r.New.Path 656 if strings.HasPrefix(r.New.Path, "./") || strings.HasPrefix(r.New.Path, "../") { 657 replacedPath = filepath.Join(modFileDir, r.New.Path) 658 } 659 660 // TODO: Is there a risk of converting a '\' delimited replacement to a '/' delimited replacement? 661 if !strings.HasPrefix(filepath.ToSlash(replacedPath)+"/", filepath.ToSlash(oldBase)+"/") { 662 continue // not affected by the package renaming 663 } 664 665 affectedReplaces = append(affectedReplaces, r) 666 } 667 668 if len(affectedReplaces) == 0 { 669 continue 670 } 671 copied, err := modfile.Parse("", pm.Mapper.Content, nil) 672 if err != nil { 673 return nil, err 674 } 675 676 for _, r := range affectedReplaces { 677 replacedPath := r.New.Path 678 if strings.HasPrefix(r.New.Path, "./") || strings.HasPrefix(r.New.Path, "../") { 679 replacedPath = filepath.Join(modFileDir, r.New.Path) 680 } 681 682 suffix := strings.TrimPrefix(replacedPath, oldBase) 683 684 newReplacedPath, err := filepath.Rel(modFileDir, newPkgDir+suffix) 685 if err != nil { 686 return nil, err 687 } 688 689 newReplacedPath = filepath.ToSlash(newReplacedPath) 690 691 if !strings.HasPrefix(newReplacedPath, "/") && !strings.HasPrefix(newReplacedPath, "../") { 692 newReplacedPath = "./" + newReplacedPath 693 } 694 695 if err := copied.AddReplace(r.Old.Path, "", newReplacedPath, ""); err != nil { 696 return nil, err 697 } 698 } 699 700 copied.Cleanup() 701 newContent, err := copied.Format() 702 if err != nil { 703 return nil, err 704 } 705 706 // Calculate the edits to be made due to the change. 707 edits := diff.Bytes(pm.Mapper.Content, newContent) 708 renamingEdits[pm.URI] = append(renamingEdits[pm.URI], edits...) 709 } 710 711 return renamingEdits, nil 712 } 713 714 // renamePackage computes all workspace edits required to rename the package 715 // described by the given metadata, to newName, by renaming its package 716 // directory. 717 // 718 // It updates package clauses and import paths for the renamed package as well 719 // as any other packages affected by the directory renaming among all packages 720 // known to the snapshot. 721 func renamePackage(ctx context.Context, s *cache.Snapshot, f file.Handle, newName PackageName) (map[protocol.DocumentURI][]diff.Edit, error) { 722 if strings.HasSuffix(string(newName), "_test") { 723 return nil, fmt.Errorf("cannot rename to _test package") 724 } 725 726 // We need metadata for the relevant package and module paths. 727 // These should be the same for all packages containing the file. 728 meta, err := NarrowestMetadataForFile(ctx, s, f.URI()) 729 if err != nil { 730 return nil, err 731 } 732 733 oldPkgPath := meta.PkgPath 734 if meta.Module == nil { 735 return nil, fmt.Errorf("cannot rename package: missing module information for package %q", meta.PkgPath) 736 } 737 modulePath := PackagePath(meta.Module.Path) 738 if modulePath == oldPkgPath { 739 return nil, fmt.Errorf("cannot rename package: module path %q is the same as the package path, so renaming the package directory would have no effect", modulePath) 740 } 741 742 newPathPrefix := path.Join(path.Dir(string(oldPkgPath)), string(newName)) 743 744 // We must inspect all packages, not just direct importers, 745 // because we also rename subpackages, which may be unrelated. 746 // (If the renamed package imports a subpackage it may require 747 // edits to both its package and import decls.) 748 allMetadata, err := s.AllMetadata(ctx) 749 if err != nil { 750 return nil, err 751 } 752 753 // Rename package and import declarations in all relevant packages. 754 edits := make(map[protocol.DocumentURI][]diff.Edit) 755 for _, mp := range allMetadata { 756 // Special case: x_test packages for the renamed package will not have the 757 // package path as a dir prefix, but still need their package clauses 758 // renamed. 759 if mp.PkgPath == oldPkgPath+"_test" { 760 if err := renamePackageClause(ctx, mp, s, newName+"_test", edits); err != nil { 761 return nil, err 762 } 763 continue 764 } 765 766 // Subtle: check this condition before checking for valid module info 767 // below, because we should not fail this operation if unrelated packages 768 // lack module info. 769 if !strings.HasPrefix(string(mp.PkgPath)+"/", string(oldPkgPath)+"/") { 770 continue // not affected by the package renaming 771 } 772 773 if mp.Module == nil { 774 // This check will always fail under Bazel. 775 return nil, fmt.Errorf("cannot rename package: missing module information for package %q", mp.PkgPath) 776 } 777 778 if modulePath != PackagePath(mp.Module.Path) { 779 continue // don't edit imports if nested package and renaming package have different module paths 780 } 781 782 // Renaming a package consists of changing its import path and package name. 783 suffix := strings.TrimPrefix(string(mp.PkgPath), string(oldPkgPath)) 784 newPath := newPathPrefix + suffix 785 786 pkgName := mp.Name 787 if mp.PkgPath == oldPkgPath { 788 pkgName = newName 789 790 if err := renamePackageClause(ctx, mp, s, newName, edits); err != nil { 791 return nil, err 792 } 793 } 794 795 imp := ImportPath(newPath) // TODO(adonovan): what if newPath has vendor/ prefix? 796 if err := renameImports(ctx, s, mp, imp, pkgName, edits); err != nil { 797 return nil, err 798 } 799 } 800 801 return edits, nil 802 } 803 804 // renamePackageClause computes edits renaming the package clause of files in 805 // the package described by the given metadata, to newName. 806 // 807 // Edits are written into the edits map. 808 func renamePackageClause(ctx context.Context, mp *metadata.Package, snapshot *cache.Snapshot, newName PackageName, edits map[protocol.DocumentURI][]diff.Edit) error { 809 // Rename internal references to the package in the renaming package. 810 for _, uri := range mp.CompiledGoFiles { 811 fh, err := snapshot.ReadFile(ctx, uri) 812 if err != nil { 813 return err 814 } 815 f, err := snapshot.ParseGo(ctx, fh, ParseHeader) 816 if err != nil { 817 return err 818 } 819 if f.File.Name == nil { 820 continue // no package declaration 821 } 822 823 edit, err := posEdit(f.Tok, f.File.Name.Pos(), f.File.Name.End(), string(newName)) 824 if err != nil { 825 return err 826 } 827 edits[f.URI] = append(edits[f.URI], edit) 828 } 829 830 return nil 831 } 832 833 // renameImports computes the set of edits to imports resulting from renaming 834 // the package described by the given metadata, to a package with import path 835 // newPath and name newName. 836 // 837 // Edits are written into the edits map. 838 func renameImports(ctx context.Context, snapshot *cache.Snapshot, mp *metadata.Package, newPath ImportPath, newName PackageName, allEdits map[protocol.DocumentURI][]diff.Edit) error { 839 rdeps, err := snapshot.ReverseDependencies(ctx, mp.ID, false) // find direct importers 840 if err != nil { 841 return err 842 } 843 844 // Pass 1: rename import paths in import declarations. 845 needsTypeCheck := make(map[PackageID][]protocol.DocumentURI) 846 for _, rdep := range rdeps { 847 if rdep.IsIntermediateTestVariant() { 848 continue // for renaming, these variants are redundant 849 } 850 851 for _, uri := range rdep.CompiledGoFiles { 852 fh, err := snapshot.ReadFile(ctx, uri) 853 if err != nil { 854 return err 855 } 856 f, err := snapshot.ParseGo(ctx, fh, ParseHeader) 857 if err != nil { 858 return err 859 } 860 if f.File.Name == nil { 861 continue // no package declaration 862 } 863 for _, imp := range f.File.Imports { 864 if rdep.DepsByImpPath[metadata.UnquoteImportPath(imp)] != mp.ID { 865 continue // not the import we're looking for 866 } 867 868 // If the import does not explicitly specify 869 // a local name, then we need to invoke the 870 // type checker to locate references to update. 871 // 872 // TODO(adonovan): is this actually true? 873 // Renaming an import with a local name can still 874 // cause conflicts: shadowing of built-ins, or of 875 // package-level decls in the same or another file. 876 if imp.Name == nil { 877 needsTypeCheck[rdep.ID] = append(needsTypeCheck[rdep.ID], uri) 878 } 879 880 // Create text edit for the import path (string literal). 881 edit, err := posEdit(f.Tok, imp.Path.Pos(), imp.Path.End(), strconv.Quote(string(newPath))) 882 if err != nil { 883 return err 884 } 885 allEdits[uri] = append(allEdits[uri], edit) 886 } 887 } 888 } 889 890 // If the imported package's name hasn't changed, 891 // we don't need to rename references within each file. 892 if newName == mp.Name { 893 return nil 894 } 895 896 // Pass 2: rename local name (types.PkgName) of imported 897 // package throughout one or more files of the package. 898 ids := make([]PackageID, 0, len(needsTypeCheck)) 899 for id := range needsTypeCheck { 900 ids = append(ids, id) 901 } 902 pkgs, err := snapshot.TypeCheck(ctx, ids...) 903 if err != nil { 904 return err 905 } 906 for i, id := range ids { 907 pkg := pkgs[i] 908 for _, uri := range needsTypeCheck[id] { 909 f, err := pkg.File(uri) 910 if err != nil { 911 return err 912 } 913 for _, imp := range f.File.Imports { 914 if imp.Name != nil { 915 continue // has explicit local name 916 } 917 if rdeps[id].DepsByImpPath[metadata.UnquoteImportPath(imp)] != mp.ID { 918 continue // not the import we're looking for 919 } 920 921 pkgname := pkg.GetTypesInfo().Implicits[imp].(*types.PkgName) 922 923 pkgScope := pkg.GetTypes().Scope() 924 fileScope := pkg.GetTypesInfo().Scopes[f.File] 925 926 localName := string(newName) 927 try := 0 928 929 // Keep trying with fresh names until one succeeds. 930 // 931 // TODO(adonovan): fix: this loop is not sufficient to choose a name 932 // that is guaranteed to be conflict-free; renameObj may still fail. 933 // So the retry loop should be around renameObj, and we shouldn't 934 // bother with scopes here. 935 for fileScope.Lookup(localName) != nil || pkgScope.Lookup(localName) != nil { 936 try++ 937 localName = fmt.Sprintf("%s%d", newName, try) 938 } 939 940 // renameObj detects various conflicts, including: 941 // - new name conflicts with a package-level decl in this file; 942 // - new name hides a package-level decl in another file that 943 // is actually referenced in this file; 944 // - new name hides a built-in that is actually referenced 945 // in this file; 946 // - a reference in this file to the old package name would 947 // become shadowed by an intervening declaration that 948 // uses the new name. 949 // It returns the edits if no conflict was detected. 950 editMap, _, err := renameObjects(localName, pkg, pkgname) 951 if err != nil { 952 return err 953 } 954 955 // If the chosen local package name matches the package's 956 // new name, delete the change that would have inserted 957 // an explicit local name, which is always the lexically 958 // first change. 959 if localName == string(newName) { 960 edits, ok := editMap[uri] 961 if !ok { 962 return fmt.Errorf("internal error: no changes for %s", uri) 963 } 964 diff.SortEdits(edits) 965 editMap[uri] = edits[1:] 966 } 967 for uri, edits := range editMap { 968 allEdits[uri] = append(allEdits[uri], edits...) 969 } 970 } 971 } 972 } 973 return nil 974 } 975 976 // renameObjects computes the edits to the type-checked syntax package pkg 977 // required to rename a set of target objects to newName. 978 // 979 // It also returns the set of objects that were found (due to 980 // corresponding methods and embedded fields) to require renaming as a 981 // consequence of the requested renamings. 982 // 983 // It returns an error if the renaming would cause a conflict. 984 func renameObjects(newName string, pkg *cache.Package, targets ...types.Object) (map[protocol.DocumentURI][]diff.Edit, map[types.Object]bool, error) { 985 r := renamer{ 986 pkg: pkg, 987 objsToUpdate: make(map[types.Object]bool), 988 from: targets[0].Name(), 989 to: newName, 990 } 991 992 // A renaming initiated at an interface method indicates the 993 // intention to rename abstract and concrete methods as needed 994 // to preserve assignability. 995 // TODO(adonovan): pull this into the caller. 996 for _, obj := range targets { 997 if obj, ok := obj.(*types.Func); ok { 998 recv := obj.Type().(*types.Signature).Recv() 999 if recv != nil && types.IsInterface(recv.Type().Underlying()) { 1000 r.changeMethods = true 1001 break 1002 } 1003 } 1004 } 1005 1006 // Check that the renaming of the identifier is ok. 1007 for _, obj := range targets { 1008 r.check(obj) 1009 if len(r.conflicts) > 0 { 1010 // Stop at first error. 1011 return nil, nil, fmt.Errorf("%s", strings.Join(r.conflicts, "\n")) 1012 } 1013 } 1014 1015 editMap, err := r.update() 1016 if err != nil { 1017 return nil, nil, err 1018 } 1019 1020 // Remove initial targets so that only 'consequences' remain. 1021 for _, obj := range targets { 1022 delete(r.objsToUpdate, obj) 1023 } 1024 return editMap, r.objsToUpdate, nil 1025 } 1026 1027 // Rename all references to the target objects. 1028 func (r *renamer) update() (map[protocol.DocumentURI][]diff.Edit, error) { 1029 result := make(map[protocol.DocumentURI][]diff.Edit) 1030 1031 // shouldUpdate reports whether obj is one of (or an 1032 // instantiation of one of) the target objects. 1033 shouldUpdate := func(obj types.Object) bool { 1034 return containsOrigin(r.objsToUpdate, obj) 1035 } 1036 1037 // Find all identifiers in the package that define or use a 1038 // renamed object. We iterate over info as it is more efficient 1039 // than calling ast.Inspect for each of r.pkg.CompiledGoFiles(). 1040 type item struct { 1041 node ast.Node // Ident, ImportSpec (obj=PkgName), or CaseClause (obj=Var) 1042 obj types.Object 1043 isDef bool 1044 } 1045 var items []item 1046 info := r.pkg.GetTypesInfo() 1047 for id, obj := range info.Uses { 1048 if shouldUpdate(obj) { 1049 items = append(items, item{id, obj, false}) 1050 } 1051 } 1052 for id, obj := range info.Defs { 1053 if shouldUpdate(obj) { 1054 items = append(items, item{id, obj, true}) 1055 } 1056 } 1057 for node, obj := range info.Implicits { 1058 if shouldUpdate(obj) { 1059 switch node.(type) { 1060 case *ast.ImportSpec, *ast.CaseClause: 1061 items = append(items, item{node, obj, true}) 1062 } 1063 } 1064 } 1065 sort.Slice(items, func(i, j int) bool { 1066 return items[i].node.Pos() < items[j].node.Pos() 1067 }) 1068 1069 // Update each identifier, and its doc comment if it is a declaration. 1070 for _, item := range items { 1071 pgf, ok := enclosingFile(r.pkg, item.node.Pos()) 1072 if !ok { 1073 bug.Reportf("edit does not belong to syntax of package %q", r.pkg) 1074 continue 1075 } 1076 1077 // Renaming a types.PkgName may result in the addition or removal of an identifier, 1078 // so we deal with this separately. 1079 if pkgName, ok := item.obj.(*types.PkgName); ok && item.isDef { 1080 edit, err := r.updatePkgName(pgf, pkgName) 1081 if err != nil { 1082 return nil, err 1083 } 1084 result[pgf.URI] = append(result[pgf.URI], edit) 1085 continue 1086 } 1087 1088 // Workaround the unfortunate lack of a Var object 1089 // for x in "switch x := expr.(type) {}" by adjusting 1090 // the case clause to the switch ident. 1091 // This may result in duplicate edits, but we de-dup later. 1092 if _, ok := item.node.(*ast.CaseClause); ok { 1093 path, _ := astutil.PathEnclosingInterval(pgf.File, item.obj.Pos(), item.obj.Pos()) 1094 item.node = path[0].(*ast.Ident) 1095 } 1096 1097 // Replace the identifier with r.to. 1098 edit, err := posEdit(pgf.Tok, item.node.Pos(), item.node.End(), r.to) 1099 if err != nil { 1100 return nil, err 1101 } 1102 1103 result[pgf.URI] = append(result[pgf.URI], edit) 1104 1105 if !item.isDef { // uses do not have doc comments to update. 1106 continue 1107 } 1108 1109 doc := docComment(pgf, item.node.(*ast.Ident)) 1110 if doc == nil { 1111 continue 1112 } 1113 1114 // Perform the rename in doc comments declared in the original package. 1115 // go/parser strips out \r\n returns from the comment text, so go 1116 // line-by-line through the comment text to get the correct positions. 1117 docRegexp := regexp.MustCompile(`\b` + r.from + `\b`) // valid identifier => valid regexp 1118 for _, comment := range doc.List { 1119 if isDirective(comment.Text) { 1120 continue 1121 } 1122 // TODO(adonovan): why are we looping over lines? 1123 // Just run the loop body once over the entire multiline comment. 1124 lines := strings.Split(comment.Text, "\n") 1125 tokFile := pgf.Tok 1126 commentLine := safetoken.Line(tokFile, comment.Pos()) 1127 uri := protocol.URIFromPath(tokFile.Name()) 1128 for i, line := range lines { 1129 lineStart := comment.Pos() 1130 if i > 0 { 1131 lineStart = tokFile.LineStart(commentLine + i) 1132 } 1133 for _, locs := range docRegexp.FindAllIndex([]byte(line), -1) { 1134 edit, err := posEdit(tokFile, lineStart+token.Pos(locs[0]), lineStart+token.Pos(locs[1]), r.to) 1135 if err != nil { 1136 return nil, err // can't happen 1137 } 1138 result[uri] = append(result[uri], edit) 1139 } 1140 } 1141 } 1142 } 1143 1144 docLinkEdits, err := r.updateCommentDocLinks() 1145 if err != nil { 1146 return nil, err 1147 } 1148 for uri, edits := range docLinkEdits { 1149 result[uri] = append(result[uri], edits...) 1150 } 1151 1152 return result, nil 1153 } 1154 1155 // updateCommentDocLinks updates each doc comment in the package 1156 // that refers to one of the renamed objects using a doc link 1157 // (https://golang.org/doc/comment#doclinks) such as "[pkg.Type.Method]". 1158 func (r *renamer) updateCommentDocLinks() (map[protocol.DocumentURI][]diff.Edit, error) { 1159 result := make(map[protocol.DocumentURI][]diff.Edit) 1160 var docRenamers []*docLinkRenamer 1161 for obj := range r.objsToUpdate { 1162 if _, ok := obj.(*types.PkgName); ok { 1163 // The dot package name will not be referenced 1164 if obj.Name() == "." { 1165 continue 1166 } 1167 1168 docRenamers = append(docRenamers, &docLinkRenamer{ 1169 isDep: false, 1170 isPkgOrType: true, 1171 file: r.pkg.FileSet().File(obj.Pos()), 1172 regexp: docLinkPattern("", "", obj.Name(), true), 1173 to: r.to, 1174 }) 1175 continue 1176 } 1177 if !obj.Exported() { 1178 continue 1179 } 1180 recvName := "" 1181 // Doc links can reference only exported package-level objects 1182 // and methods of exported package-level named types. 1183 if !isPackageLevel(obj) { 1184 _, isFunc := obj.(*types.Func) 1185 if !isFunc { 1186 continue 1187 } 1188 recv := obj.Type().(*types.Signature).Recv() 1189 if recv == nil { 1190 continue 1191 } 1192 recvT := recv.Type() 1193 if ptr, ok := recvT.(*types.Pointer); ok { 1194 recvT = ptr.Elem() 1195 } 1196 named, isNamed := recvT.(*types.Named) 1197 if !isNamed { 1198 continue 1199 } 1200 // Doc links can't reference interface methods. 1201 if types.IsInterface(named.Underlying()) { 1202 continue 1203 } 1204 name := named.Origin().Obj() 1205 if !name.Exported() || !isPackageLevel(name) { 1206 continue 1207 } 1208 recvName = name.Name() 1209 } 1210 1211 // Qualify objects from other packages. 1212 pkgName := "" 1213 if r.pkg.GetTypes() != obj.Pkg() { 1214 pkgName = obj.Pkg().Name() 1215 } 1216 _, isTypeName := obj.(*types.TypeName) 1217 docRenamers = append(docRenamers, &docLinkRenamer{ 1218 isDep: r.pkg.GetTypes() != obj.Pkg(), 1219 isPkgOrType: isTypeName, 1220 packagePath: obj.Pkg().Path(), 1221 packageName: pkgName, 1222 recvName: recvName, 1223 objName: obj.Name(), 1224 regexp: docLinkPattern(pkgName, recvName, obj.Name(), isTypeName), 1225 to: r.to, 1226 }) 1227 } 1228 for _, pgf := range r.pkg.CompiledGoFiles() { 1229 for _, d := range docRenamers { 1230 edits, err := d.update(pgf) 1231 if err != nil { 1232 return nil, err 1233 } 1234 if len(edits) > 0 { 1235 result[pgf.URI] = append(result[pgf.URI], edits...) 1236 } 1237 } 1238 } 1239 return result, nil 1240 } 1241 1242 // docLinkPattern returns a regular expression that matches doclinks in comments. 1243 // It has one submatch that indicates the symbol to be updated. 1244 func docLinkPattern(pkgName, recvName, objName string, isPkgOrType bool) *regexp.Regexp { 1245 // The doc link may contain a leading star, e.g. [*bytes.Buffer]. 1246 pattern := `\[\*?` 1247 if pkgName != "" { 1248 pattern += pkgName + `\.` 1249 } 1250 if recvName != "" { 1251 pattern += recvName + `\.` 1252 } 1253 // The first submatch is object name. 1254 pattern += `(` + objName + `)` 1255 // If the object is a *types.TypeName or *types.PkgName, also need 1256 // match the objects referenced by them, so add `(\.\w+)*`. 1257 if isPkgOrType { 1258 pattern += `(?:\.\w+)*` 1259 } 1260 // There are two type of link in comments: 1261 // 1. url link. e.g. [text]: url 1262 // 2. doc link. e.g. [pkg.Name] 1263 // in order to only match the doc link, add `([^:]|$)` in the end. 1264 pattern += `\](?:[^:]|$)` 1265 1266 return regexp.MustCompile(pattern) 1267 } 1268 1269 // A docLinkRenamer renames doc links of forms such as these: 1270 // 1271 // [Func] 1272 // [pkg.Func] 1273 // [RecvType.Method] 1274 // [*Type] 1275 // [*pkg.Type] 1276 // [*pkg.RecvType.Method] 1277 type docLinkRenamer struct { 1278 isDep bool // object is from a dependency package 1279 isPkgOrType bool // object is *types.PkgName or *types.TypeName 1280 packagePath string 1281 packageName string // e.g. "pkg" 1282 recvName string // e.g. "RecvType" 1283 objName string // e.g. "Func", "Type", "Method" 1284 to string // new name 1285 regexp *regexp.Regexp 1286 1287 file *token.File // enclosing file, if renaming *types.PkgName 1288 } 1289 1290 // update updates doc links in the package level comments. 1291 func (r *docLinkRenamer) update(pgf *parsego.File) (result []diff.Edit, err error) { 1292 if r.file != nil && r.file != pgf.Tok { 1293 return nil, nil 1294 } 1295 pattern := r.regexp 1296 // If the object is in dependency package, 1297 // the imported name in the file may be different from the original package name 1298 if r.isDep { 1299 for _, spec := range pgf.File.Imports { 1300 importPath, _ := strconv.Unquote(spec.Path.Value) 1301 if importPath == r.packagePath { 1302 // Ignore blank imports 1303 if spec.Name == nil || spec.Name.Name == "_" || spec.Name.Name == "." { 1304 continue 1305 } 1306 if spec.Name.Name != r.packageName { 1307 pattern = docLinkPattern(spec.Name.Name, r.recvName, r.objName, r.isPkgOrType) 1308 } 1309 break 1310 } 1311 } 1312 } 1313 1314 var edits []diff.Edit 1315 updateDocLinks := func(doc *ast.CommentGroup) error { 1316 if doc != nil { 1317 for _, c := range doc.List { 1318 for _, locs := range pattern.FindAllStringSubmatchIndex(c.Text, -1) { 1319 // The first submatch is the object name, so the locs[2:4] is the index of object name. 1320 edit, err := posEdit(pgf.Tok, c.Pos()+token.Pos(locs[2]), c.Pos()+token.Pos(locs[3]), r.to) 1321 if err != nil { 1322 return err 1323 } 1324 edits = append(edits, edit) 1325 } 1326 } 1327 } 1328 return nil 1329 } 1330 1331 // Update package doc comments. 1332 err = updateDocLinks(pgf.File.Doc) 1333 if err != nil { 1334 return nil, err 1335 } 1336 for _, decl := range pgf.File.Decls { 1337 var doc *ast.CommentGroup 1338 switch decl := decl.(type) { 1339 case *ast.GenDecl: 1340 doc = decl.Doc 1341 case *ast.FuncDecl: 1342 doc = decl.Doc 1343 } 1344 err = updateDocLinks(doc) 1345 if err != nil { 1346 return nil, err 1347 } 1348 } 1349 return edits, nil 1350 } 1351 1352 // docComment returns the doc for an identifier within the specified file. 1353 func docComment(pgf *ParsedGoFile, id *ast.Ident) *ast.CommentGroup { 1354 nodes, _ := astutil.PathEnclosingInterval(pgf.File, id.Pos(), id.End()) 1355 for _, node := range nodes { 1356 switch decl := node.(type) { 1357 case *ast.FuncDecl: 1358 return decl.Doc 1359 case *ast.Field: 1360 return decl.Doc 1361 case *ast.GenDecl: 1362 return decl.Doc 1363 // For {Type,Value}Spec, if the doc on the spec is absent, 1364 // search for the enclosing GenDecl 1365 case *ast.TypeSpec: 1366 if decl.Doc != nil { 1367 return decl.Doc 1368 } 1369 case *ast.ValueSpec: 1370 if decl.Doc != nil { 1371 return decl.Doc 1372 } 1373 case *ast.Ident: 1374 case *ast.AssignStmt: 1375 // *ast.AssignStmt doesn't have an associated comment group. 1376 // So, we try to find a comment just before the identifier. 1377 1378 // Try to find a comment group only for short variable declarations (:=). 1379 if decl.Tok != token.DEFINE { 1380 return nil 1381 } 1382 1383 identLine := safetoken.Line(pgf.Tok, id.Pos()) 1384 for _, comment := range nodes[len(nodes)-1].(*ast.File).Comments { 1385 if comment.Pos() > id.Pos() { 1386 // Comment is after the identifier. 1387 continue 1388 } 1389 1390 lastCommentLine := safetoken.Line(pgf.Tok, comment.End()) 1391 if lastCommentLine+1 == identLine { 1392 return comment 1393 } 1394 } 1395 default: 1396 return nil 1397 } 1398 } 1399 return nil 1400 } 1401 1402 // updatePkgName returns the updates to rename a pkgName in the import spec by 1403 // only modifying the package name portion of the import declaration. 1404 func (r *renamer) updatePkgName(pgf *ParsedGoFile, pkgName *types.PkgName) (diff.Edit, error) { 1405 // Modify ImportSpec syntax to add or remove the Name as needed. 1406 path, _ := astutil.PathEnclosingInterval(pgf.File, pkgName.Pos(), pkgName.Pos()) 1407 if len(path) < 2 { 1408 return diff.Edit{}, fmt.Errorf("no path enclosing interval for %s", pkgName.Name()) 1409 } 1410 spec, ok := path[1].(*ast.ImportSpec) 1411 if !ok { 1412 return diff.Edit{}, fmt.Errorf("failed to update PkgName for %s", pkgName.Name()) 1413 } 1414 1415 newText := "" 1416 if pkgName.Imported().Name() != r.to { 1417 newText = r.to + " " 1418 } 1419 1420 // Replace the portion (possibly empty) of the spec before the path: 1421 // local "path" or "path" 1422 // -> <- -><- 1423 return posEdit(pgf.Tok, spec.Pos(), spec.Path.Pos(), newText) 1424 } 1425 1426 // parsePackageNameDecl is a convenience function that parses and 1427 // returns the package name declaration of file fh, and reports 1428 // whether the position ppos lies within it. 1429 // 1430 // Note: also used by references. 1431 func parsePackageNameDecl(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, ppos protocol.Position) (*ParsedGoFile, bool, error) { 1432 pgf, err := snapshot.ParseGo(ctx, fh, ParseHeader) 1433 if err != nil { 1434 return nil, false, err 1435 } 1436 // Careful: because we used ParseHeader, 1437 // pgf.Pos(ppos) may be beyond EOF => (0, err). 1438 pos, _ := pgf.PositionPos(ppos) 1439 return pgf, pgf.File.Name.Pos() <= pos && pos <= pgf.File.Name.End(), nil 1440 } 1441 1442 // enclosingFile returns the CompiledGoFile of pkg that contains the specified position. 1443 func enclosingFile(pkg *cache.Package, pos token.Pos) (*ParsedGoFile, bool) { 1444 for _, pgf := range pkg.CompiledGoFiles() { 1445 if pgf.File.Pos() <= pos && pos <= pgf.File.End() { 1446 return pgf, true 1447 } 1448 } 1449 return nil, false 1450 } 1451 1452 // posEdit returns an edit to replace the (start, end) range of tf with 'new'. 1453 func posEdit(tf *token.File, start, end token.Pos, new string) (diff.Edit, error) { 1454 startOffset, endOffset, err := safetoken.Offsets(tf, start, end) 1455 if err != nil { 1456 return diff.Edit{}, err 1457 } 1458 return diff.Edit{Start: startOffset, End: endOffset, New: new}, nil 1459 }