Skip to content

Commit 1a08d01

Browse files
author
Dung Le
committed
gopls/internal/lsp: update replace directives in go.mod for package renaming
Check active go.mod files in the workspace to see if any replace directives need to be fixed if package renaming affects the replaced locations in any go.mod files. For golang/go#56184. Change-Id: I98ea07a602c39168d13f42f1b7a5f6738e194ece Reviewed-on: https://go-review.googlesource.com/c/tools/+/459416 Reviewed-by: Robert Findley <[email protected]> Run-TryBot: Dylan Le <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent eac36cb commit 1a08d01

File tree

2 files changed

+115
-3
lines changed

2 files changed

+115
-3
lines changed

gopls/internal/lsp/source/rename.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import (
1212
"go/token"
1313
"go/types"
1414
"path"
15+
"path/filepath"
1516
"regexp"
1617
"sort"
1718
"strconv"
1819
"strings"
1920

21+
"golang.org/x/mod/modfile"
2022
"golang.org/x/tools/go/types/typeutil"
2123
"golang.org/x/tools/gopls/internal/lsp/protocol"
2224
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -214,6 +216,92 @@ func Rename(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position,
214216
return nil, true, err
215217
}
216218

219+
oldBase := filepath.Dir(span.URI.Filename(f.URI()))
220+
newPkgDir := filepath.Join(filepath.Dir(oldBase), newName)
221+
222+
// TODO: should this operate on all go.mod files, irrespective of whether they are included in the workspace?
223+
// Get all active mod files in the workspace
224+
modFiles := s.ModFiles()
225+
for _, m := range modFiles {
226+
fh, err := s.GetFile(ctx, m)
227+
if err != nil {
228+
return nil, true, err
229+
}
230+
pm, err := s.ParseMod(ctx, fh)
231+
if err != nil {
232+
return nil, true, err
233+
}
234+
235+
modFileDir := filepath.Dir(pm.URI.Filename())
236+
affectedReplaces := []*modfile.Replace{}
237+
238+
// Check if any replace directives need to be fixed
239+
for _, r := range pm.File.Replace {
240+
if !strings.HasPrefix(r.New.Path, "/") && !strings.HasPrefix(r.New.Path, "./") && !strings.HasPrefix(r.New.Path, "../") {
241+
continue
242+
}
243+
244+
replacedPath := r.New.Path
245+
if strings.HasPrefix(r.New.Path, "./") || strings.HasPrefix(r.New.Path, "../") {
246+
replacedPath = filepath.Join(modFileDir, r.New.Path)
247+
}
248+
249+
// TODO: Is there a risk of converting a '\' delimited replacement to a '/' delimited replacement?
250+
if !strings.HasPrefix(filepath.ToSlash(replacedPath)+"/", filepath.ToSlash(oldBase)+"/") {
251+
continue // not affected by the package renaming
252+
}
253+
254+
affectedReplaces = append(affectedReplaces, r)
255+
}
256+
257+
if len(affectedReplaces) == 0 {
258+
continue
259+
}
260+
copied, err := modfile.Parse("", pm.Mapper.Content, nil)
261+
if err != nil {
262+
return nil, true, err
263+
}
264+
265+
for _, r := range affectedReplaces {
266+
replacedPath := r.New.Path
267+
if strings.HasPrefix(r.New.Path, "./") || strings.HasPrefix(r.New.Path, "../") {
268+
replacedPath = filepath.Join(modFileDir, r.New.Path)
269+
}
270+
271+
suffix := strings.TrimPrefix(replacedPath, string(oldBase))
272+
273+
newReplacedPath, err := filepath.Rel(modFileDir, newPkgDir+suffix)
274+
if err != nil {
275+
return nil, true, err
276+
}
277+
278+
newReplacedPath = filepath.ToSlash(newReplacedPath)
279+
280+
if !strings.HasPrefix(newReplacedPath, "/") && !strings.HasPrefix(newReplacedPath, "../") {
281+
newReplacedPath = "./" + newReplacedPath
282+
}
283+
284+
if err := copied.AddReplace(r.Old.Path, "", newReplacedPath, ""); err != nil {
285+
return nil, true, err
286+
}
287+
}
288+
289+
copied.Cleanup()
290+
newContent, err := copied.Format()
291+
if err != nil {
292+
return nil, true, err
293+
}
294+
295+
// Calculate the edits to be made due to the change.
296+
diff := s.View().Options().ComputeEdits(string(pm.Mapper.Content), string(newContent))
297+
modFileEdits, err := ToProtocolEdits(pm.Mapper, diff)
298+
if err != nil {
299+
return nil, true, err
300+
}
301+
302+
renamingEdits[pm.URI] = append(renamingEdits[pm.URI], modFileEdits...)
303+
}
304+
217305
return renamingEdits, true, nil
218306
}
219307

gopls/internal/regtest/misc/rename_test.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,8 +519,16 @@ func main() {
519519
}
520520

521521
func TestRenamePackage_NestedModule(t *testing.T) {
522-
testenv.NeedsGo1Point(t, 17)
522+
testenv.NeedsGo1Point(t, 18)
523523
const files = `
524+
-- go.work --
525+
go 1.18
526+
use (
527+
.
528+
./foo/bar
529+
./foo/baz
530+
)
531+
524532
-- go.mod --
525533
module mod.com
526534
@@ -530,7 +538,10 @@ require (
530538
mod.com/foo/bar v0.0.0
531539
)
532540
533-
replace mod.com/foo/bar => ./foo/bar
541+
replace (
542+
mod.com/foo/bar => ./foo/bar
543+
mod.com/foo/baz => ./foo/baz
544+
)
534545
-- foo/foo.go --
535546
package foo
536547
@@ -546,20 +557,30 @@ module mod.com/foo/bar
546557
-- foo/bar/bar.go --
547558
package bar
548559
549-
const Msg = "Hi"
560+
const Msg = "Hi from package bar"
561+
562+
-- foo/baz/go.mod --
563+
module mod.com/foo/baz
564+
565+
-- foo/baz/baz.go --
566+
package baz
567+
568+
const Msg = "Hi from package baz"
550569
551570
-- main.go --
552571
package main
553572
554573
import (
555574
"fmt"
556575
"mod.com/foo/bar"
576+
"mod.com/foo/baz"
557577
"mod.com/foo"
558578
)
559579
560580
func main() {
561581
foo.Bar()
562582
fmt.Println(bar.Msg)
583+
fmt.Println(baz.Msg)
563584
}
564585
`
565586
Run(t, files, func(t *testing.T, env *Env) {
@@ -574,6 +595,9 @@ func main() {
574595
env.RegexpSearch("main.go", "mod.com/foo/bar")
575596
env.RegexpSearch("main.go", "mod.com/foox")
576597
env.RegexpSearch("main.go", "foox.Bar()")
598+
599+
env.RegexpSearch("go.mod", "./foox/bar")
600+
env.RegexpSearch("go.mod", "./foox/baz")
577601
})
578602
}
579603

0 commit comments

Comments
 (0)