Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 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 | |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 5 | package source |
| 6 | |
| 7 | import ( |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 8 | "bytes" |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 9 | "context" |
Robert Findley | 37590b3 | 2022-04-19 18:08:06 -0400 | [diff] [blame] | 10 | "errors" |
| 11 | "fmt" |
Suzy Mueller | 70d3714 | 2019-06-20 15:24:17 -0400 | [diff] [blame] | 12 | "go/ast" |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 13 | "go/format" |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 14 | "go/token" |
| 15 | "go/types" |
Suzy Mueller | 70d3714 | 2019-06-20 15:24:17 -0400 | [diff] [blame] | 16 | "regexp" |
Rebecca Stambler | 55a0fde | 2020-07-02 00:57:55 -0400 | [diff] [blame] | 17 | "strings" |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 18 | |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 19 | "golang.org/x/tools/go/types/typeutil" |
Ian Cottrell | cf0cb92 | 2020-04-17 09:32:56 -0400 | [diff] [blame] | 20 | "golang.org/x/tools/internal/event" |
Ian Cottrell | 85edb9e | 2019-08-19 19:28:08 -0400 | [diff] [blame] | 21 | "golang.org/x/tools/internal/lsp/diff" |
Suzy Mueller | c940306 | 2019-08-22 13:31:03 -0400 | [diff] [blame] | 22 | "golang.org/x/tools/internal/lsp/protocol" |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 23 | "golang.org/x/tools/internal/span" |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 24 | "golang.org/x/tools/refactor/satisfy" |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 25 | ) |
| 26 | |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 27 | type renamer struct { |
Rebecca Stambler | 252024b | 2019-06-21 17:00:02 -0400 | [diff] [blame] | 28 | ctx context.Context |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 29 | fset *token.FileSet |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 30 | refs []*ReferenceInfo |
| 31 | objsToUpdate map[types.Object]bool |
| 32 | hadConflicts bool |
| 33 | errors string |
| 34 | from, to string |
| 35 | satisfyConstraints map[satisfy.Constraint]bool |
Dan Kortschak | 4077921 | 2022-03-12 10:35:13 +1030 | [diff] [blame] | 36 | packages map[*types.Package]Package // may include additional packages that are a dep of pkg |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 37 | msets typeutil.MethodSetCache |
| 38 | changeMethods bool |
| 39 | } |
| 40 | |
Suzy Mueller | c940306 | 2019-08-22 13:31:03 -0400 | [diff] [blame] | 41 | type PrepareItem struct { |
Rebecca Stambler | 8159a2d | 2019-09-05 20:04:28 -0400 | [diff] [blame] | 42 | Range protocol.Range |
Suzy Mueller | c940306 | 2019-08-22 13:31:03 -0400 | [diff] [blame] | 43 | Text string |
| 44 | } |
| 45 | |
Rob Findley | c2bea79 | 2021-01-15 15:46:22 -0500 | [diff] [blame] | 46 | // PrepareRename searches for a valid renaming at position pp. |
| 47 | // |
| 48 | // The returned usererr is intended to be displayed to the user to explain why |
| 49 | // the prepare fails. Probably we could eliminate the redundancy in returning |
| 50 | // two errors, but for now this is done defensively. |
| 51 | func PrepareRename(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position) (_ *PrepareItem, usererr, err error) { |
Dylan Le | 9580c84 | 2022-07-21 15:23:17 -0400 | [diff] [blame] | 52 | fileRenameSupported := false |
| 53 | for _, op := range snapshot.View().Options().SupportedResourceOperations { |
| 54 | if op == protocol.Rename { |
| 55 | fileRenameSupported = true |
| 56 | break |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | // Find position of the package name declaration |
| 61 | pgf, err := snapshot.ParseGo(ctx, f, ParseFull) |
| 62 | if err != nil { |
| 63 | return nil, err, err |
| 64 | } |
| 65 | inPackageName, err := isInPackageName(ctx, snapshot, f, pgf, pp) |
| 66 | if err != nil { |
| 67 | return nil, err, err |
| 68 | } |
| 69 | |
| 70 | if inPackageName && !fileRenameSupported { |
| 71 | err := errors.New("can't rename packages: LSP client does not support file renaming") |
| 72 | return nil, err, err |
| 73 | } |
| 74 | |
Ian Cottrell | 7b212d6 | 2020-04-20 12:14:12 -0400 | [diff] [blame] | 75 | ctx, done := event.Start(ctx, "source.PrepareRename") |
Suzy Mueller | c940306 | 2019-08-22 13:31:03 -0400 | [diff] [blame] | 76 | defer done() |
| 77 | |
Rob Findley | 6932d22 | 2021-08-06 15:36:09 -0400 | [diff] [blame] | 78 | qos, err := qualifiedObjsAtProtocolPos(ctx, snapshot, f.URI(), pp) |
Rebecca Stambler | 8159a2d | 2019-09-05 20:04:28 -0400 | [diff] [blame] | 79 | if err != nil { |
Rob Findley | c2bea79 | 2021-01-15 15:46:22 -0500 | [diff] [blame] | 80 | return nil, nil, err |
Rebecca Stambler | 8159a2d | 2019-09-05 20:04:28 -0400 | [diff] [blame] | 81 | } |
Rebecca Stambler | eca45d4 | 2020-03-26 23:25:15 -0400 | [diff] [blame] | 82 | node, obj, pkg := qos[0].node, qos[0].obj, qos[0].sourcePkg |
Rob Findley | c2bea79 | 2021-01-15 15:46:22 -0500 | [diff] [blame] | 83 | if err := checkRenamable(obj); err != nil { |
| 84 | return nil, err, err |
| 85 | } |
Heschi Kreinick | f29cbc7 | 2020-07-28 17:00:10 -0400 | [diff] [blame] | 86 | mr, err := posToMappedRange(snapshot, pkg, node.Pos(), node.End()) |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 87 | if err != nil { |
Rob Findley | c2bea79 | 2021-01-15 15:46:22 -0500 | [diff] [blame] | 88 | return nil, nil, err |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 89 | } |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 90 | rng, err := mr.Range() |
| 91 | if err != nil { |
Rob Findley | c2bea79 | 2021-01-15 15:46:22 -0500 | [diff] [blame] | 92 | return nil, nil, err |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 93 | } |
Rebecca Stambler | eca45d4 | 2020-03-26 23:25:15 -0400 | [diff] [blame] | 94 | if _, isImport := node.(*ast.ImportSpec); isImport { |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 95 | // We're not really renaming the import path. |
| 96 | rng.End = rng.Start |
| 97 | } |
Suzy Mueller | c940306 | 2019-08-22 13:31:03 -0400 | [diff] [blame] | 98 | return &PrepareItem{ |
Rebecca Stambler | 8159a2d | 2019-09-05 20:04:28 -0400 | [diff] [blame] | 99 | Range: rng, |
Rebecca Stambler | eca45d4 | 2020-03-26 23:25:15 -0400 | [diff] [blame] | 100 | Text: obj.Name(), |
Rob Findley | c2bea79 | 2021-01-15 15:46:22 -0500 | [diff] [blame] | 101 | }, nil, nil |
Suzy Mueller | c940306 | 2019-08-22 13:31:03 -0400 | [diff] [blame] | 102 | } |
| 103 | |
Rob Findley | c2bea79 | 2021-01-15 15:46:22 -0500 | [diff] [blame] | 104 | // checkRenamable verifies if an obj may be renamed. |
| 105 | func checkRenamable(obj types.Object) error { |
| 106 | if v, ok := obj.(*types.Var); ok && v.Embedded() { |
| 107 | return errors.New("can't rename embedded fields: rename the type directly or name the field") |
| 108 | } |
| 109 | if obj.Name() == "_" { |
| 110 | return errors.New("can't rename \"_\"") |
| 111 | } |
| 112 | return nil |
| 113 | } |
| 114 | |
| 115 | // Rename returns a map of TextEdits for each file modified when renaming a |
| 116 | // given identifier within a package. |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 117 | func Rename(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position, newName string) (map[span.URI][]protocol.TextEdit, error) { |
Ian Cottrell | 7b212d6 | 2020-04-20 12:14:12 -0400 | [diff] [blame] | 118 | ctx, done := event.Start(ctx, "source.Rename") |
Ian Cottrell | 75aaaba | 2019-06-26 22:46:12 -0400 | [diff] [blame] | 119 | defer done() |
Rebecca Stambler | b667c4c | 2019-07-11 21:05:55 -0400 | [diff] [blame] | 120 | |
Dylan Le | bd3f524 | 2022-07-29 16:50:30 -0400 | [diff] [blame] | 121 | pgf, err := s.ParseGo(ctx, f, ParseFull) |
| 122 | if err != nil { |
| 123 | return nil, err |
| 124 | } |
| 125 | inPackageName, err := isInPackageName(ctx, s, f, pgf, pp) |
| 126 | if err != nil { |
| 127 | return nil, err |
| 128 | } |
| 129 | |
| 130 | if inPackageName { |
| 131 | renamingPkg, err := s.PackageForFile(ctx, f.URI(), TypecheckAll, NarrowestPackage) |
| 132 | if err != nil { |
| 133 | return nil, err |
| 134 | } |
| 135 | |
| 136 | result := make(map[span.URI][]protocol.TextEdit) |
| 137 | // Rename internal references to the package in the renaming package |
| 138 | // Todo(dle): need more investigation on case when pkg.GoFiles != pkg.CompiledGoFiles if using cgo. |
| 139 | for _, f := range renamingPkg.CompiledGoFiles() { |
| 140 | pkgNameMappedRange := NewMappedRange(f.Tok, f.Mapper, f.File.Name.Pos(), f.File.Name.End()) |
| 141 | rng, err := pkgNameMappedRange.Range() |
| 142 | if err != nil { |
| 143 | return nil, err |
| 144 | } |
| 145 | result[f.URI] = []protocol.TextEdit{ |
| 146 | { |
| 147 | Range: rng, |
| 148 | NewText: newName, |
| 149 | }, |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | return result, nil |
| 154 | } |
| 155 | |
Rob Findley | 6932d22 | 2021-08-06 15:36:09 -0400 | [diff] [blame] | 156 | qos, err := qualifiedObjsAtProtocolPos(ctx, s, f.URI(), pp) |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 157 | if err != nil { |
| 158 | return nil, err |
| 159 | } |
| 160 | |
Rob Findley | c2bea79 | 2021-01-15 15:46:22 -0500 | [diff] [blame] | 161 | obj, pkg := qos[0].obj, qos[0].pkg |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 162 | |
Rob Findley | c2bea79 | 2021-01-15 15:46:22 -0500 | [diff] [blame] | 163 | if err := checkRenamable(obj); err != nil { |
| 164 | return nil, err |
| 165 | } |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 166 | if obj.Name() == newName { |
Robert Findley | 37590b3 | 2022-04-19 18:08:06 -0400 | [diff] [blame] | 167 | return nil, fmt.Errorf("old and new names are the same: %s", newName) |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 168 | } |
| 169 | if !isValidIdentifier(newName) { |
Robert Findley | 37590b3 | 2022-04-19 18:08:06 -0400 | [diff] [blame] | 170 | return nil, fmt.Errorf("invalid identifier to rename: %q", newName) |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 171 | } |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 172 | if pkg == nil || pkg.IsIllTyped() { |
Robert Findley | 37590b3 | 2022-04-19 18:08:06 -0400 | [diff] [blame] | 173 | return nil, fmt.Errorf("package for %s is ill typed", f.URI()) |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 174 | } |
Rob Findley | 45115c1 | 2021-01-10 11:10:32 -0500 | [diff] [blame] | 175 | refs, err := references(ctx, s, qos, true, false, true) |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 176 | if err != nil { |
| 177 | return nil, err |
| 178 | } |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 179 | r := renamer{ |
Suzy Mueller | 6cfa556 | 2019-06-27 14:09:03 -0400 | [diff] [blame] | 180 | ctx: ctx, |
Heschi Kreinick | f29cbc7 | 2020-07-28 17:00:10 -0400 | [diff] [blame] | 181 | fset: s.FileSet(), |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 182 | refs: refs, |
| 183 | objsToUpdate: make(map[types.Object]bool), |
Muir Manders | f80fb1d | 2019-12-17 21:06:31 -0800 | [diff] [blame] | 184 | from: obj.Name(), |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 185 | to: newName, |
| 186 | packages: make(map[*types.Package]Package), |
| 187 | } |
Rebecca Stambler | f8240f7 | 2020-07-12 00:26:29 -0400 | [diff] [blame] | 188 | |
| 189 | // A renaming initiated at an interface method indicates the |
| 190 | // intention to rename abstract and concrete methods as needed |
| 191 | // to preserve assignability. |
| 192 | for _, ref := range refs { |
| 193 | if obj, ok := ref.obj.(*types.Func); ok { |
| 194 | recv := obj.Type().(*types.Signature).Recv() |
Danish Dua | acefd22 | 2020-09-04 15:37:47 -0400 | [diff] [blame] | 195 | if recv != nil && IsInterface(recv.Type().Underlying()) { |
Rebecca Stambler | f8240f7 | 2020-07-12 00:26:29 -0400 | [diff] [blame] | 196 | r.changeMethods = true |
| 197 | break |
| 198 | } |
| 199 | } |
| 200 | } |
Suzy Mueller | 7b25e35 | 2019-07-08 18:53:01 -0700 | [diff] [blame] | 201 | for _, from := range refs { |
Suzy Mueller | 9065c18 | 2019-08-15 10:29:18 -0400 | [diff] [blame] | 202 | r.packages[from.pkg.GetTypes()] = from.pkg |
Suzy Mueller | 7b25e35 | 2019-07-08 18:53:01 -0700 | [diff] [blame] | 203 | } |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 204 | |
| 205 | // Check that the renaming of the identifier is ok. |
Suzy Mueller | 9065c18 | 2019-08-15 10:29:18 -0400 | [diff] [blame] | 206 | for _, ref := range refs { |
| 207 | r.check(ref.obj) |
Suzy Mueller | d5940c8 | 2019-08-16 12:23:59 -0400 | [diff] [blame] | 208 | if r.hadConflicts { // one error is enough. |
| 209 | break |
| 210 | } |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 211 | } |
| 212 | if r.hadConflicts { |
Robert Findley | 37590b3 | 2022-04-19 18:08:06 -0400 | [diff] [blame] | 213 | return nil, fmt.Errorf(r.errors) |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 214 | } |
| 215 | |
Suzy Mueller | a0f5e6c | 2019-07-15 17:02:40 -0400 | [diff] [blame] | 216 | changes, err := r.update() |
| 217 | if err != nil { |
| 218 | return nil, err |
| 219 | } |
Dylan Le | bd3f524 | 2022-07-29 16:50:30 -0400 | [diff] [blame] | 220 | |
Rebecca Stambler | 2ca7180 | 2019-09-06 14:55:14 -0400 | [diff] [blame] | 221 | result := make(map[span.URI][]protocol.TextEdit) |
| 222 | for uri, edits := range changes { |
Rebecca Stambler | 2dc213d | 2019-09-16 18:17:51 -0400 | [diff] [blame] | 223 | // These edits should really be associated with FileHandles for maximal correctness. |
| 224 | // For now, this is good enough. |
Heschi Kreinick | ecd3fc4 | 2020-06-08 15:21:24 -0400 | [diff] [blame] | 225 | fh, err := s.GetFile(ctx, uri) |
Rebecca Stambler | 2ca7180 | 2019-09-06 14:55:14 -0400 | [diff] [blame] | 226 | if err != nil { |
| 227 | return nil, err |
| 228 | } |
Heschi Kreinick | ecd3fc4 | 2020-06-08 15:21:24 -0400 | [diff] [blame] | 229 | data, err := fh.Read() |
Rebecca Stambler | 2dc213d | 2019-09-16 18:17:51 -0400 | [diff] [blame] | 230 | if err != nil { |
| 231 | return nil, err |
| 232 | } |
Robert Findley | 9d7bf95 | 2022-05-12 23:23:17 -0400 | [diff] [blame] | 233 | m := protocol.NewColumnMapper(uri, data) |
Rebecca Stambler | 2dc213d | 2019-09-16 18:17:51 -0400 | [diff] [blame] | 234 | // Sort the edits first. |
| 235 | diff.SortTextEdits(edits) |
Rebecca Stambler | 2ca7180 | 2019-09-06 14:55:14 -0400 | [diff] [blame] | 236 | protocolEdits, err := ToProtocolEdits(m, edits) |
| 237 | if err != nil { |
| 238 | return nil, err |
| 239 | } |
| 240 | result[uri] = protocolEdits |
Suzy Mueller | a0f5e6c | 2019-07-15 17:02:40 -0400 | [diff] [blame] | 241 | } |
Rebecca Stambler | 2ca7180 | 2019-09-06 14:55:14 -0400 | [diff] [blame] | 242 | return result, nil |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 243 | } |
| 244 | |
| 245 | // Rename all references to the identifier. |
Ian Cottrell | 85edb9e | 2019-08-19 19:28:08 -0400 | [diff] [blame] | 246 | func (r *renamer) update() (map[span.URI][]diff.TextEdit, error) { |
| 247 | result := make(map[span.URI][]diff.TextEdit) |
Suzy Mueller | 7b25e35 | 2019-07-08 18:53:01 -0700 | [diff] [blame] | 248 | seen := make(map[span.Span]bool) |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 249 | |
Edward Muller | d1f6565 | 2019-06-24 21:48:30 +0000 | [diff] [blame] | 250 | docRegexp, err := regexp.Compile(`\b` + r.from + `\b`) |
| 251 | if err != nil { |
| 252 | return nil, err |
| 253 | } |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 254 | for _, ref := range r.refs { |
Robert Findley | ea60815 | 2022-05-23 18:50:04 -0400 | [diff] [blame] | 255 | refSpan, err := ref.Span() |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 256 | if err != nil { |
| 257 | return nil, err |
| 258 | } |
Suzy Mueller | 7b25e35 | 2019-07-08 18:53:01 -0700 | [diff] [blame] | 259 | if seen[refSpan] { |
| 260 | continue |
| 261 | } |
| 262 | seen[refSpan] = true |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 263 | |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 264 | // Renaming a types.PkgName may result in the addition or removal of an identifier, |
| 265 | // so we deal with this separately. |
| 266 | if pkgName, ok := ref.obj.(*types.PkgName); ok && ref.isDeclaration { |
| 267 | edit, err := r.updatePkgName(pkgName) |
| 268 | if err != nil { |
| 269 | return nil, err |
| 270 | } |
| 271 | result[refSpan.URI()] = append(result[refSpan.URI()], *edit) |
| 272 | continue |
| 273 | } |
| 274 | |
| 275 | // Replace the identifier with r.to. |
Ian Cottrell | 85edb9e | 2019-08-19 19:28:08 -0400 | [diff] [blame] | 276 | edit := diff.TextEdit{ |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 277 | Span: refSpan, |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 278 | NewText: r.to, |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 279 | } |
Suzy Mueller | 6cfa556 | 2019-06-27 14:09:03 -0400 | [diff] [blame] | 280 | |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 281 | result[refSpan.URI()] = append(result[refSpan.URI()], edit) |
Suzy Mueller | 70d3714 | 2019-06-20 15:24:17 -0400 | [diff] [blame] | 282 | |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 283 | if !ref.isDeclaration || ref.ident == nil { // uses do not have doc comments to update. |
Edward Muller | 1a55b15 | 2019-06-27 16:17:07 +0000 | [diff] [blame] | 284 | continue |
Suzy Mueller | 70d3714 | 2019-06-20 15:24:17 -0400 | [diff] [blame] | 285 | } |
| 286 | |
Suzy Mueller | 9065c18 | 2019-08-15 10:29:18 -0400 | [diff] [blame] | 287 | doc := r.docComment(ref.pkg, ref.ident) |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 288 | if doc == nil { |
Edward Muller | 1a55b15 | 2019-06-27 16:17:07 +0000 | [diff] [blame] | 289 | continue |
| 290 | } |
| 291 | |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 292 | // Perform the rename in doc comments declared in the original package. |
Rebecca Stambler | 55a0fde | 2020-07-02 00:57:55 -0400 | [diff] [blame] | 293 | // go/parser strips out \r\n returns from the comment text, so go |
| 294 | // line-by-line through the comment text to get the correct positions. |
Edward Muller | 1a55b15 | 2019-06-27 16:17:07 +0000 | [diff] [blame] | 295 | for _, comment := range doc.List { |
Rebecca Stambler | c64668f | 2020-11-02 22:59:26 -0500 | [diff] [blame] | 296 | if isDirective(comment.Text) { |
| 297 | continue |
| 298 | } |
Rebecca Stambler | 55a0fde | 2020-07-02 00:57:55 -0400 | [diff] [blame] | 299 | lines := strings.Split(comment.Text, "\n") |
Alan Donovan | b929f3b | 2022-06-01 15:20:56 -0400 | [diff] [blame] | 300 | tokFile := r.fset.File(comment.Pos()) |
| 301 | commentLine := tokFile.Line(comment.Pos()) |
Rebecca Stambler | 55a0fde | 2020-07-02 00:57:55 -0400 | [diff] [blame] | 302 | for i, line := range lines { |
| 303 | lineStart := comment.Pos() |
| 304 | if i > 0 { |
Alan Donovan | b929f3b | 2022-06-01 15:20:56 -0400 | [diff] [blame] | 305 | lineStart = tokFile.LineStart(commentLine + i) |
Edward Muller | 1a55b15 | 2019-06-27 16:17:07 +0000 | [diff] [blame] | 306 | } |
Rebecca Stambler | 55a0fde | 2020-07-02 00:57:55 -0400 | [diff] [blame] | 307 | for _, locs := range docRegexp.FindAllIndex([]byte(line), -1) { |
Alan Donovan | b929f3b | 2022-06-01 15:20:56 -0400 | [diff] [blame] | 308 | rng := span.NewRange(tokFile, lineStart+token.Pos(locs[0]), lineStart+token.Pos(locs[1])) |
Rebecca Stambler | 55a0fde | 2020-07-02 00:57:55 -0400 | [diff] [blame] | 309 | spn, err := rng.Span() |
| 310 | if err != nil { |
| 311 | return nil, err |
| 312 | } |
| 313 | result[spn.URI()] = append(result[spn.URI()], diff.TextEdit{ |
| 314 | Span: spn, |
| 315 | NewText: r.to, |
| 316 | }) |
| 317 | } |
Edward Muller | 1a55b15 | 2019-06-27 16:17:07 +0000 | [diff] [blame] | 318 | } |
| 319 | } |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 320 | } |
| 321 | |
Suzy Mueller | 1fa5683 | 2019-06-11 15:09:43 -0400 | [diff] [blame] | 322 | return result, nil |
Suzy Mueller | 4adf7a7 | 2019-06-18 10:23:37 -0400 | [diff] [blame] | 323 | } |
Suzy Mueller | 70d3714 | 2019-06-20 15:24:17 -0400 | [diff] [blame] | 324 | |
| 325 | // docComment returns the doc for an identifier. |
| 326 | func (r *renamer) docComment(pkg Package, id *ast.Ident) *ast.CommentGroup { |
Alan Donovan | b929f3b | 2022-06-01 15:20:56 -0400 | [diff] [blame] | 327 | _, tokFile, nodes, _ := pathEnclosingInterval(r.fset, pkg, id.Pos(), id.End()) |
Suzy Mueller | 70d3714 | 2019-06-20 15:24:17 -0400 | [diff] [blame] | 328 | for _, node := range nodes { |
| 329 | switch decl := node.(type) { |
| 330 | case *ast.FuncDecl: |
| 331 | return decl.Doc |
| 332 | case *ast.Field: |
| 333 | return decl.Doc |
| 334 | case *ast.GenDecl: |
| 335 | return decl.Doc |
| 336 | // For {Type,Value}Spec, if the doc on the spec is absent, |
| 337 | // search for the enclosing GenDecl |
| 338 | case *ast.TypeSpec: |
| 339 | if decl.Doc != nil { |
| 340 | return decl.Doc |
| 341 | } |
| 342 | case *ast.ValueSpec: |
| 343 | if decl.Doc != nil { |
| 344 | return decl.Doc |
| 345 | } |
| 346 | case *ast.Ident: |
Shoshin Nikita | ccff732 | 2021-06-11 16:53:29 +0000 | [diff] [blame] | 347 | case *ast.AssignStmt: |
| 348 | // *ast.AssignStmt doesn't have an associated comment group. |
| 349 | // So, we try to find a comment just before the identifier. |
| 350 | |
| 351 | // Try to find a comment group only for short variable declarations (:=). |
| 352 | if decl.Tok != token.DEFINE { |
| 353 | return nil |
| 354 | } |
| 355 | |
Alan Donovan | b929f3b | 2022-06-01 15:20:56 -0400 | [diff] [blame] | 356 | identLine := tokFile.Line(id.Pos()) |
| 357 | for _, comment := range nodes[len(nodes)-1].(*ast.File).Comments { |
Shoshin Nikita | ccff732 | 2021-06-11 16:53:29 +0000 | [diff] [blame] | 358 | if comment.Pos() > id.Pos() { |
| 359 | // Comment is after the identifier. |
| 360 | continue |
| 361 | } |
| 362 | |
Alan Donovan | b929f3b | 2022-06-01 15:20:56 -0400 | [diff] [blame] | 363 | lastCommentLine := tokFile.Line(comment.End()) |
Shoshin Nikita | ccff732 | 2021-06-11 16:53:29 +0000 | [diff] [blame] | 364 | if lastCommentLine+1 == identLine { |
| 365 | return comment |
| 366 | } |
| 367 | } |
Suzy Mueller | 70d3714 | 2019-06-20 15:24:17 -0400 | [diff] [blame] | 368 | default: |
| 369 | return nil |
| 370 | } |
| 371 | } |
| 372 | return nil |
| 373 | } |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 374 | |
| 375 | // updatePkgName returns the updates to rename a pkgName in the import spec |
Ian Cottrell | 85edb9e | 2019-08-19 19:28:08 -0400 | [diff] [blame] | 376 | func (r *renamer) updatePkgName(pkgName *types.PkgName) (*diff.TextEdit, error) { |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 377 | // Modify ImportSpec syntax to add or remove the Name as needed. |
| 378 | pkg := r.packages[pkgName.Pkg()] |
Alan Donovan | b929f3b | 2022-06-01 15:20:56 -0400 | [diff] [blame] | 379 | _, tokFile, path, _ := pathEnclosingInterval(r.fset, pkg, pkgName.Pos(), pkgName.Pos()) |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 380 | if len(path) < 2 { |
Robert Findley | 37590b3 | 2022-04-19 18:08:06 -0400 | [diff] [blame] | 381 | return nil, fmt.Errorf("no path enclosing interval for %s", pkgName.Name()) |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 382 | } |
| 383 | spec, ok := path[1].(*ast.ImportSpec) |
| 384 | if !ok { |
Robert Findley | 37590b3 | 2022-04-19 18:08:06 -0400 | [diff] [blame] | 385 | return nil, fmt.Errorf("failed to update PkgName for %s", pkgName.Name()) |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 386 | } |
| 387 | |
| 388 | var astIdent *ast.Ident // will be nil if ident is removed |
| 389 | if pkgName.Imported().Name() != r.to { |
| 390 | // ImportSpec.Name needed |
| 391 | astIdent = &ast.Ident{NamePos: spec.Path.Pos(), Name: r.to} |
| 392 | } |
| 393 | |
| 394 | // Make a copy of the ident that just has the name and path. |
| 395 | updated := &ast.ImportSpec{ |
| 396 | Name: astIdent, |
| 397 | Path: spec.Path, |
| 398 | EndPos: spec.EndPos, |
| 399 | } |
| 400 | |
Alan Donovan | b929f3b | 2022-06-01 15:20:56 -0400 | [diff] [blame] | 401 | rng := span.NewRange(tokFile, spec.Pos(), spec.End()) |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 402 | spn, err := rng.Span() |
| 403 | if err != nil { |
| 404 | return nil, err |
| 405 | } |
| 406 | |
| 407 | var buf bytes.Buffer |
| 408 | format.Node(&buf, r.fset, updated) |
| 409 | newText := buf.String() |
| 410 | |
Ian Cottrell | 85edb9e | 2019-08-19 19:28:08 -0400 | [diff] [blame] | 411 | return &diff.TextEdit{ |
Suzy Mueller | f80f671 | 2019-06-27 14:01:56 -0400 | [diff] [blame] | 412 | Span: spn, |
| 413 | NewText: newText, |
| 414 | }, nil |
| 415 | } |