Skip to content

Commit 84fe542

Browse files
committed
gopls/internal/golang: allow package move into empty directories
Previously, we would prevent moving a package if the resulting package directory already exists. Instead, we should allow the move as long as the target directory is empty -- either it doesn't exist or it contains only directory entries. Change-Id: I6590148d9d0fc1a57a8a4b13d39412fc7965f33c Reviewed-on: https://go-review.googlesource.com/c/tools/+/721680 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alan Donovan <adonovan@google.com>
1 parent e58dfd3 commit 84fe542

File tree

3 files changed

+59
-27
lines changed

3 files changed

+59
-27
lines changed

gopls/internal/golang/rename_check.go

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ package golang
3333
// method, not the concrete method.
3434

3535
import (
36+
"errors"
3637
"fmt"
3738
"go/ast"
3839
"go/build"
@@ -948,10 +949,12 @@ func checkPackageRename(opts *settings.Options, curPkg *cache.Package, f file.Ha
948949
// path, in which case we should not allow renaming.
949950
root := filepath.Dir(f.URI().DirPath())
950951
newPkgDir = filepath.Join(root, newName)
951-
_, err := os.Stat(newPkgDir)
952-
if err == nil {
953-
// Directory already exists, return an error.
954-
return "", "", "", fmt.Errorf("invalid package identifier: %q already exists", newName)
952+
empty, err := dirIsEmpty(newPkgDir)
953+
if err != nil {
954+
return "", "", "", err
955+
}
956+
if !empty {
957+
return "", "", "", fmt.Errorf("invalid package identifier: %q is not empty", newName)
955958
}
956959
parentPkgPath := strings.TrimSuffix(string(curPkg.Metadata().PkgPath), string(curPkg.Metadata().Name)) // leaves a trailing slash
957960
newPkgPath = PackagePath(parentPkgPath + newName)
@@ -977,10 +980,12 @@ func checkPackageRename(opts *settings.Options, curPkg *cache.Package, f file.Ha
977980
return "", "", "", fmt.Errorf("invalid package path %q", newName)
978981
}
979982
newPkgName = PackageName(filepath.Base(newPkgDir))
980-
_, err = os.Stat(newPkgDir)
981-
if err == nil {
982-
// Directory or file already exists at this path; return an error.
983-
return "", "", "", fmt.Errorf("invalid package path: %q already exists", newName)
983+
empty, err := dirIsEmpty(newPkgDir)
984+
if err != nil {
985+
return "", "", "", err
986+
}
987+
if !empty {
988+
return "", "", "", fmt.Errorf("invalid package path: %q is not empty", newName)
984989
}
985990
// Verify that the new package name is a valid identifier.
986991
if !isValidIdentifier(string(newPkgName)) {
@@ -1001,6 +1006,25 @@ func hasIgnoredGoFiles(pkg *cache.Package) bool {
10011006
return false
10021007
}
10031008

1009+
// dirIsEmpty returns true if the directory does not exist or if the entries in
1010+
// dir consist of only directories.
1011+
func dirIsEmpty(dir string) (bool, error) {
1012+
files, err := os.ReadDir(dir)
1013+
if err != nil {
1014+
if errors.Is(err, fs.ErrNotExist) {
1015+
return true, nil
1016+
} else {
1017+
return false, err
1018+
}
1019+
}
1020+
for _, f := range files {
1021+
if !f.IsDir() {
1022+
return false, nil
1023+
}
1024+
}
1025+
return true, nil
1026+
}
1027+
10041028
// isLocal reports whether obj is local to some function.
10051029
// Precondition: not a struct field or interface method.
10061030
func isLocal(obj types.Object) bool {

gopls/internal/test/integration/misc/rename_test.go

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,7 @@ const A = 1 + nested.B
848848

849849
for _, badName := range []string{"$$$", "lib_test"} {
850850
if err := env.Editor.Rename(env.Ctx, loc, badName); err == nil {
851-
t.Errorf("Rename(lib, libx) succeeded, want non-nil error")
851+
t.Errorf("Rename(lib, libx) succeeded unexpectedly")
852852
}
853853
}
854854
})
@@ -926,17 +926,19 @@ package test2
926926
WithOptions(Settings{"packageMove": true}).Run(t, files, func(t *testing.T, env *Env) {
927927
env.OpenFile("test1/test1.go")
928928
loc := env.RegexpSearch("test1/test1.go", "test1")
929-
badNames := []string{"test2", "mod.com/test2", "differentmod.com/test2",
930-
"mod.com/$$$", "mod*.$+", "mod.commmm"}
931-
expectedErrMatches := []string{"invalid package identifier", "already exists",
932-
"cannot move package across module boundary", "invalid package name",
933-
"invalid package path", "cannot move package across module boundary"}
934-
for i, badName := range badNames {
935-
err := env.Editor.Rename(env.Ctx, loc, badName)
929+
for _, test := range []struct{ badName, expectedErrMatch string }{
930+
{"test2", "invalid package identifier"},
931+
{"mod.com/test2", "not empty"},
932+
{"differentmod.com/test2", "cannot move package across module boundary"},
933+
{"mod.com/$$$", "invalid package name"},
934+
{"mod*.$+", "invalid package path"},
935+
{"mod.commmm", "cannot move package across module boundary"},
936+
} {
937+
err := env.Editor.Rename(env.Ctx, loc, test.badName)
936938
if err == nil {
937-
t.Errorf("Rename to %s succeeded, want non-nil error", badName)
938-
} else if !strings.Contains(err.Error(), expectedErrMatches[i]) {
939-
t.Errorf("Rename to %s produced incorrect error message, got %s, want %s", badName, err.Error(), expectedErrMatches[i])
939+
t.Errorf("Rename to %s succeeded unexpectedly", test.badName)
940+
} else if !strings.Contains(err.Error(), test.expectedErrMatch) {
941+
t.Errorf("Rename to %s produced incorrect error message, got %s, want %s", test.badName, err.Error(), test.expectedErrMatch)
940942
}
941943
}
942944
})
@@ -958,15 +960,18 @@ package test2
958960
WithOptions(Settings{"packageMove": false}).Run(t, files, func(t *testing.T, env *Env) {
959961
env.OpenFile("test1/test1.go")
960962
loc := env.RegexpSearch("test1/test1.go", "test1")
961-
badNames := []string{"test2", "mod.com/test3"}
962-
expectedErrMatches := []string{"invalid package identifier", "setting 'packageMove' is not enabled"}
963-
for i, badName := range badNames {
964-
err := env.Editor.Rename(env.Ctx, loc, badName)
963+
964+
for _, test := range []struct{ badName, expectedErrMatch string }{
965+
{"test2", "invalid package identifier"},
966+
{"mod.com/test3", "setting 'packageMove' is not enabled"},
967+
} {
968+
err := env.Editor.Rename(env.Ctx, loc, test.badName)
965969
if err == nil {
966-
t.Errorf("Rename to %s succeeded, want non-nil error", badName)
967-
} else if !strings.Contains(err.Error(), expectedErrMatches[i]) {
968-
t.Errorf("Rename to %s produced incorrect error message, got %s, want %s", badName, err.Error(), expectedErrMatches[i])
970+
t.Errorf("Rename to %s succeeded unexpectedly", test.badName)
971+
} else if !strings.Contains(err.Error(), test.expectedErrMatch) {
972+
t.Errorf("Rename to %s produced incorrect error message, got %s, want %s", test.badName, err.Error(), test.expectedErrMatch)
969973
}
974+
970975
}
971976
})
972977
}

gopls/internal/test/marker/testdata/rename/packagedecl.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ go 1.20
3434
package one //@ rename("one", "golang.org/lsptests/rename/one", sameName), rename("one", "golang.org/lsptests/rename/two", newNameSameDir), renameerr("one", "golang.org/lsptests/otherdir/one", re"cannot move package across module boundary")
3535

3636
-- three/three.go --
37-
package three //@ renameerr("three", "golang.org/lsptests/othermod/three", re"cannot move package across module boundary"), renameerr("three", "golang.org/lsptests/rename/one", re"already exists")
37+
package three //@ renameerr("three", "golang.org/lsptests/othermod/three", re"cannot move package across module boundary"), renameerr("three", "golang.org/lsptests/rename/one", re"not empty")
3838

3939

4040
-- six/six.go --
@@ -91,6 +91,9 @@ import (
9191
-- seven/eight/other/other.go --
9292
package other //@ rename("other", "golang.org/lsptests/rename/seven/eight/other/newDir1/newDir2", createNewDir)
9393

94+
-- seven/eight/other/newDir1/newDir2/newDir3/test.go --
95+
package newDir3 // verifies that we can move the package above into a directory that already exists but is empty
96+
9497
-- fix/fix_test.go --
9598
package fix_test
9699
-- othermoddir/othermod/other.go --

0 commit comments

Comments
 (0)