aboutsummaryrefslogtreecommitdiff
path: root/gopls/internal/regtest/misc/rename_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'gopls/internal/regtest/misc/rename_test.go')
-rw-r--r--gopls/internal/regtest/misc/rename_test.go885
1 files changed, 881 insertions, 4 deletions
diff --git a/gopls/internal/regtest/misc/rename_test.go b/gopls/internal/regtest/misc/rename_test.go
index 121b70725..ebb02609d 100644
--- a/gopls/internal/regtest/misc/rename_test.go
+++ b/gopls/internal/regtest/misc/rename_test.go
@@ -5,12 +5,319 @@
package misc
import (
+ "fmt"
"strings"
"testing"
- . "golang.org/x/tools/internal/lsp/regtest"
+ "golang.org/x/tools/gopls/internal/lsp/protocol"
+ . "golang.org/x/tools/gopls/internal/lsp/regtest"
+ "golang.org/x/tools/gopls/internal/lsp/tests/compare"
+ "golang.org/x/tools/internal/testenv"
+)
+
+func TestPrepareRenameMainPackage(t *testing.T) {
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- main.go --
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ fmt.Println(1)
+}
+`
+ const wantErr = "can't rename package \"main\""
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("main.go")
+ loc := env.RegexpSearch("main.go", `main`)
+ params := &protocol.PrepareRenameParams{
+ TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc),
+ }
+ _, err := env.Editor.Server.PrepareRename(env.Ctx, params)
+ if err == nil {
+ t.Errorf("missing can't rename package main error from PrepareRename")
+ }
+
+ if err.Error() != wantErr {
+ t.Errorf("got %v, want %v", err.Error(), wantErr)
+ }
+ })
+}
+
+// Test case for golang/go#56227
+func TestRenameWithUnsafeSlice(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17) // unsafe.Slice was added in Go 1.17
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- p.go --
+package p
+
+import "unsafe"
+
+type T struct{}
+
+func (T) M() {}
+
+func _() {
+ x := [3]int{1, 2, 3}
+ ptr := unsafe.Pointer(&x)
+ _ = unsafe.Slice((*int)(ptr), 3)
+}
+`
+
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("p.go")
+ env.Rename(env.RegexpSearch("p.go", "M"), "N") // must not panic
+ })
+}
+
+func TestPrepareRenameWithNoPackageDeclaration(t *testing.T) {
+ const files = `
+go 1.14
+-- lib/a.go --
+import "fmt"
+
+const A = 1
+
+func bar() {
+ fmt.Println("Bar")
+}
+
+-- main.go --
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello")
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ err := env.Editor.Rename(env.Ctx, env.RegexpSearch("lib/a.go", "fmt"), "fmt1")
+ if got, want := fmt.Sprint(err), "no identifier found"; got != want {
+ t.Errorf("Rename: got error %v, want %v", got, want)
+ }
+ })
+}
+
+func TestPrepareRenameFailWithUnknownModule(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+go 1.14
+-- lib/a.go --
+package lib
+
+const A = 1
+
+-- main.go --
+package main
+
+import (
+ "mod.com/lib"
+)
+
+func main() {
+ println("Hello")
+}
+`
+ const wantErr = "can't rename package: missing module information for package"
+ Run(t, files, func(t *testing.T, env *Env) {
+ loc := env.RegexpSearch("lib/a.go", "lib")
+ params := &protocol.PrepareRenameParams{
+ TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc),
+ }
+ _, err := env.Editor.Server.PrepareRename(env.Ctx, params)
+ if err == nil || !strings.Contains(err.Error(), wantErr) {
+ t.Errorf("missing cannot rename packages with unknown module from PrepareRename")
+ }
+ })
+}
+
+// This test ensures that each import of a renamed package
+// is also renamed if it would otherwise create a conflict.
+func TestRenamePackageWithConflicts(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+const A = 1
+
+-- lib/nested/a.go --
+package nested
+
+const B = 1
+
+-- lib/x/a.go --
+package nested1
+
+const C = 1
+
+-- main.go --
+package main
+
+import (
+ "mod.com/lib"
+ "mod.com/lib/nested"
+ nested1 "mod.com/lib/x"
+)
+
+func main() {
+ println("Hello")
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ env.Rename(env.RegexpSearch("lib/a.go", "lib"), "nested")
+
+ // Check if the new package name exists.
+ env.RegexpSearch("nested/a.go", "package nested")
+ env.RegexpSearch("main.go", `nested2 "mod.com/nested"`)
+ env.RegexpSearch("main.go", "mod.com/nested/nested")
+ env.RegexpSearch("main.go", `nested1 "mod.com/nested/x"`)
+ })
+}
+
+func TestRenamePackageWithAlias(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+const A = 1
+
+-- lib/nested/a.go --
+package nested
+
+const B = 1
+
+-- main.go --
+package main
+
+import (
+ "mod.com/lib"
+ lib1 "mod.com/lib/nested"
)
+func main() {
+ println("Hello")
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ env.Rename(env.RegexpSearch("lib/a.go", "lib"), "nested")
+
+ // Check if the new package name exists.
+ env.RegexpSearch("nested/a.go", "package nested")
+ env.RegexpSearch("main.go", "mod.com/nested")
+ env.RegexpSearch("main.go", `lib1 "mod.com/nested/nested"`)
+ })
+}
+
+func TestRenamePackageWithDifferentDirectoryPath(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+const A = 1
+
+-- lib/nested/a.go --
+package foo
+
+const B = 1
+
+-- main.go --
+package main
+
+import (
+ "mod.com/lib"
+ foo "mod.com/lib/nested"
+)
+
+func main() {
+ println("Hello")
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ env.Rename(env.RegexpSearch("lib/a.go", "lib"), "nested")
+
+ // Check if the new package name exists.
+ env.RegexpSearch("nested/a.go", "package nested")
+ env.RegexpSearch("main.go", "mod.com/nested")
+ env.RegexpSearch("main.go", `foo "mod.com/nested/nested"`)
+ })
+}
+
+func TestRenamePackage(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+const A = 1
+
+-- lib/b.go --
+package lib
+
+const B = 1
+
+-- lib/nested/a.go --
+package nested
+
+const C = 1
+
+-- main.go --
+package main
+
+import (
+ "mod.com/lib"
+ "mod.com/lib/nested"
+)
+
+func main() {
+ println("Hello")
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ env.Rename(env.RegexpSearch("lib/a.go", "lib"), "lib1")
+
+ // Check if the new package name exists.
+ env.RegexpSearch("lib1/a.go", "package lib1")
+ env.RegexpSearch("lib1/b.go", "package lib1")
+ env.RegexpSearch("main.go", "mod.com/lib1")
+ env.RegexpSearch("main.go", "mod.com/lib1/nested")
+ })
+}
+
// Test for golang/go#47564.
func TestRenameInTestVariant(t *testing.T) {
const files = `
@@ -48,11 +355,581 @@ func main() {
Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("main.go")
- pos := env.RegexpSearch("main.go", `stringutil\.(Identity)`)
- env.Rename("main.go", pos, "Identityx")
- text := env.Editor.BufferText("stringutil/stringutil_test.go")
+ env.Rename(env.RegexpSearch("main.go", `stringutil\.(Identity)`), "Identityx")
+ env.OpenFile("stringutil/stringutil_test.go")
+ text := env.BufferText("stringutil/stringutil_test.go")
if !strings.Contains(text, "Identityx") {
t.Errorf("stringutil/stringutil_test.go: missing expected token `Identityx` after rename:\n%s", text)
}
})
}
+
+// This is a test that rename operation initiated by the editor function as expected.
+func TestRenameFileFromEditor(t *testing.T) {
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.16
+-- a/a.go --
+package a
+
+const X = 1
+-- a/x.go --
+package a
+
+const X = 2
+-- b/b.go --
+package b
+`
+
+ Run(t, files, func(t *testing.T, env *Env) {
+ // Rename files and verify that diagnostics are affected accordingly.
+
+ // Initially, we should have diagnostics on both X's, for their duplicate declaration.
+ env.OnceMet(
+ InitialWorkspaceLoad,
+ Diagnostics(env.AtRegexp("a/a.go", "X")),
+ Diagnostics(env.AtRegexp("a/x.go", "X")),
+ )
+
+ // Moving x.go should make the diagnostic go away.
+ env.RenameFile("a/x.go", "b/x.go")
+ env.AfterChange(
+ NoDiagnostics(ForFile("a/a.go")), // no more duplicate declarations
+ Diagnostics(env.AtRegexp("b/b.go", "package")), // as package names mismatch
+ )
+
+ // Renaming should also work on open buffers.
+ env.OpenFile("b/x.go")
+
+ // Moving x.go back to a/ should cause the diagnostics to reappear.
+ env.RenameFile("b/x.go", "a/x.go")
+ env.AfterChange(
+ Diagnostics(env.AtRegexp("a/a.go", "X")),
+ Diagnostics(env.AtRegexp("a/x.go", "X")),
+ )
+
+ // Renaming the entire directory should move both the open and closed file.
+ env.RenameFile("a", "x")
+ env.AfterChange(
+ Diagnostics(env.AtRegexp("x/a.go", "X")),
+ Diagnostics(env.AtRegexp("x/x.go", "X")),
+ )
+
+ // As a sanity check, verify that x/x.go is open.
+ if text := env.BufferText("x/x.go"); text == "" {
+ t.Fatal("got empty buffer for x/x.go")
+ }
+ })
+}
+
+func TestRenamePackage_Tests(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+const A = 1
+
+-- lib/b.go --
+package lib
+
+const B = 1
+
+-- lib/a_test.go --
+package lib_test
+
+import (
+ "mod.com/lib"
+ "fmt
+)
+
+const C = 1
+
+-- lib/b_test.go --
+package lib
+
+import (
+ "fmt
+)
+
+const D = 1
+
+-- lib/nested/a.go --
+package nested
+
+const D = 1
+
+-- main.go --
+package main
+
+import (
+ "mod.com/lib"
+ "mod.com/lib/nested"
+)
+
+func main() {
+ println("Hello")
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ env.Rename(env.RegexpSearch("lib/a.go", "lib"), "lib1")
+
+ // Check if the new package name exists.
+ env.RegexpSearch("lib1/a.go", "package lib1")
+ env.RegexpSearch("lib1/b.go", "package lib1")
+ env.RegexpSearch("main.go", "mod.com/lib1")
+ env.RegexpSearch("main.go", "mod.com/lib1/nested")
+
+ // Check if the test package is renamed
+ env.RegexpSearch("lib1/a_test.go", "package lib1_test")
+ env.RegexpSearch("lib1/b_test.go", "package lib1")
+ })
+}
+
+func TestRenamePackage_NestedModule(t *testing.T) {
+ testenv.NeedsGo1Point(t, 18)
+ const files = `
+-- go.work --
+go 1.18
+use (
+ .
+ ./foo/bar
+ ./foo/baz
+)
+
+-- go.mod --
+module mod.com
+
+go 1.18
+
+require (
+ mod.com/foo/bar v0.0.0
+)
+
+replace (
+ mod.com/foo/bar => ./foo/bar
+ mod.com/foo/baz => ./foo/baz
+)
+-- foo/foo.go --
+package foo
+
+import "fmt"
+
+func Bar() {
+ fmt.Println("In foo before renamed to foox.")
+}
+
+-- foo/bar/go.mod --
+module mod.com/foo/bar
+
+-- foo/bar/bar.go --
+package bar
+
+const Msg = "Hi from package bar"
+
+-- foo/baz/go.mod --
+module mod.com/foo/baz
+
+-- foo/baz/baz.go --
+package baz
+
+const Msg = "Hi from package baz"
+
+-- main.go --
+package main
+
+import (
+ "fmt"
+ "mod.com/foo/bar"
+ "mod.com/foo/baz"
+ "mod.com/foo"
+)
+
+func main() {
+ foo.Bar()
+ fmt.Println(bar.Msg)
+ fmt.Println(baz.Msg)
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("foo/foo.go")
+ env.Rename(env.RegexpSearch("foo/foo.go", "foo"), "foox")
+
+ env.RegexpSearch("foox/foo.go", "package foox")
+ env.OpenFile("foox/bar/bar.go")
+ env.OpenFile("foox/bar/go.mod")
+
+ env.RegexpSearch("main.go", "mod.com/foo/bar")
+ env.RegexpSearch("main.go", "mod.com/foox")
+ env.RegexpSearch("main.go", "foox.Bar()")
+
+ env.RegexpSearch("go.mod", "./foox/bar")
+ env.RegexpSearch("go.mod", "./foox/baz")
+ })
+}
+
+func TestRenamePackage_DuplicateImport(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+const A = 1
+
+-- lib/nested/a.go --
+package nested
+
+const B = 1
+
+-- main.go --
+package main
+
+import (
+ "mod.com/lib"
+ lib1 "mod.com/lib"
+ lib2 "mod.com/lib/nested"
+)
+
+func main() {
+ println("Hello")
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ env.Rename(env.RegexpSearch("lib/a.go", "lib"), "nested")
+
+ // Check if the new package name exists.
+ env.RegexpSearch("nested/a.go", "package nested")
+ env.RegexpSearch("main.go", "mod.com/nested")
+ env.RegexpSearch("main.go", `lib1 "mod.com/nested"`)
+ env.RegexpSearch("main.go", `lib2 "mod.com/nested/nested"`)
+ })
+}
+
+func TestRenamePackage_DuplicateBlankImport(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+const A = 1
+
+-- lib/nested/a.go --
+package nested
+
+const B = 1
+
+-- main.go --
+package main
+
+import (
+ "mod.com/lib"
+ _ "mod.com/lib"
+ lib1 "mod.com/lib/nested"
+)
+
+func main() {
+ println("Hello")
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ env.Rename(env.RegexpSearch("lib/a.go", "lib"), "nested")
+
+ // Check if the new package name exists.
+ env.RegexpSearch("nested/a.go", "package nested")
+ env.RegexpSearch("main.go", "mod.com/nested")
+ env.RegexpSearch("main.go", `_ "mod.com/nested"`)
+ env.RegexpSearch("main.go", `lib1 "mod.com/nested/nested"`)
+ })
+}
+
+func TestRenamePackage_TestVariant(t *testing.T) {
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- foo/foo.go --
+package foo
+
+const Foo = 42
+-- bar/bar.go --
+package bar
+
+import "mod.com/foo"
+
+const Bar = foo.Foo
+-- bar/bar_test.go --
+package bar
+
+import "mod.com/foo"
+
+const Baz = foo.Foo
+-- testdata/bar/bar.go --
+package bar
+
+import "mod.com/foox"
+
+const Bar = foox.Foo
+-- testdata/bar/bar_test.go --
+package bar
+
+import "mod.com/foox"
+
+const Baz = foox.Foo
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("foo/foo.go")
+ env.Rename(env.RegexpSearch("foo/foo.go", "package (foo)"), "foox")
+
+ checkTestdata(t, env)
+ })
+}
+
+func TestRenamePackage_IntermediateTestVariant(t *testing.T) {
+ // In this test set up, we have the following import edges:
+ // bar_test -> baz -> foo -> bar
+ // bar_test -> foo -> bar
+ // bar_test -> bar
+ //
+ // As a consequence, bar_x_test.go is in the reverse closure of both
+ // `foo [bar.test]` and `baz [bar.test]`. This test confirms that we don't
+ // produce duplicate edits in this case.
+ const files = `
+-- go.mod --
+module foo.mod
+
+go 1.12
+-- foo/foo.go --
+package foo
+
+import "foo.mod/bar"
+
+const Foo = 42
+
+const _ = bar.Bar
+-- baz/baz.go --
+package baz
+
+import "foo.mod/foo"
+
+const Baz = foo.Foo
+-- bar/bar.go --
+package bar
+
+var Bar = 123
+-- bar/bar_test.go --
+package bar
+
+const _ = Bar
+-- bar/bar_x_test.go --
+package bar_test
+
+import (
+ "foo.mod/bar"
+ "foo.mod/baz"
+ "foo.mod/foo"
+)
+
+const _ = bar.Bar + baz.Baz + foo.Foo
+-- testdata/foox/foo.go --
+package foox
+
+import "foo.mod/bar"
+
+const Foo = 42
+
+const _ = bar.Bar
+-- testdata/baz/baz.go --
+package baz
+
+import "foo.mod/foox"
+
+const Baz = foox.Foo
+-- testdata/bar/bar_x_test.go --
+package bar_test
+
+import (
+ "foo.mod/bar"
+ "foo.mod/baz"
+ "foo.mod/foox"
+)
+
+const _ = bar.Bar + baz.Baz + foox.Foo
+`
+
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("foo/foo.go")
+ env.Rename(env.RegexpSearch("foo/foo.go", "package (foo)"), "foox")
+
+ checkTestdata(t, env)
+ })
+}
+
+func TestRenamePackage_Nesting(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+import "mod.com/lib/nested"
+
+const A = 1 + nested.B
+-- lib/nested/a.go --
+package nested
+
+const B = 1
+-- other/other.go --
+package other
+
+import (
+ "mod.com/lib"
+ "mod.com/lib/nested"
+)
+
+const C = lib.A + nested.B
+-- testdata/libx/a.go --
+package libx
+
+import "mod.com/libx/nested"
+
+const A = 1 + nested.B
+-- testdata/other/other.go --
+package other
+
+import (
+ "mod.com/libx"
+ "mod.com/libx/nested"
+)
+
+const C = libx.A + nested.B
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ env.Rename(env.RegexpSearch("lib/a.go", "package (lib)"), "libx")
+
+ checkTestdata(t, env)
+ })
+}
+
+func TestRenamePackage_InvalidName(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+import "mod.com/lib/nested"
+
+const A = 1 + nested.B
+`
+
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/a.go")
+ loc := env.RegexpSearch("lib/a.go", "package (lib)")
+
+ for _, badName := range []string{"$$$", "lib_test"} {
+ if err := env.Editor.Rename(env.Ctx, loc, badName); err == nil {
+ t.Errorf("Rename(lib, libx) succeeded, want non-nil error")
+ }
+ }
+ })
+}
+
+func TestRenamePackage_InternalPackage(t *testing.T) {
+ testenv.NeedsGo1Point(t, 17)
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- lib/a.go --
+package lib
+
+import (
+ "fmt"
+ "mod.com/lib/internal/x"
+)
+
+const A = 1
+
+func print() {
+ fmt.Println(x.B)
+}
+
+-- lib/internal/x/a.go --
+package x
+
+const B = 1
+
+-- main.go --
+package main
+
+import "mod.com/lib"
+
+func main() {
+ lib.print()
+}
+`
+ Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("lib/internal/x/a.go")
+ env.Rename(env.RegexpSearch("lib/internal/x/a.go", "x"), "utils")
+
+ // Check if the new package name exists.
+ env.RegexpSearch("lib/a.go", "mod.com/lib/internal/utils")
+ env.RegexpSearch("lib/a.go", "utils.B")
+
+ // Check if the test package is renamed
+ env.RegexpSearch("lib/internal/utils/a.go", "package utils")
+
+ env.OpenFile("lib/a.go")
+ env.Rename(env.RegexpSearch("lib/a.go", "lib"), "lib1")
+
+ // Check if the new package name exists.
+ env.RegexpSearch("lib1/a.go", "package lib1")
+ env.RegexpSearch("lib1/a.go", "mod.com/lib1/internal/utils")
+ env.RegexpSearch("main.go", `import "mod.com/lib1"`)
+ env.RegexpSearch("main.go", "lib1.print()")
+ })
+}
+
+// checkTestdata checks that current buffer contents match their corresponding
+// expected content in the testdata directory.
+func checkTestdata(t *testing.T, env *Env) {
+ t.Helper()
+ files := env.ListFiles("testdata")
+ if len(files) == 0 {
+ t.Fatal("no files in testdata directory")
+ }
+ for _, file := range files {
+ suffix := strings.TrimPrefix(file, "testdata/")
+ got := env.BufferText(suffix)
+ want := env.ReadWorkspaceFile(file)
+ if diff := compare.Text(want, got); diff != "" {
+ t.Errorf("Rename: unexpected buffer content for %s (-want +got):\n%s", suffix, diff)
+ }
+ }
+}