From dc34f68349891eb2f67b1a37a933b780d83d5fe1 Mon Sep 17 00:00:00 2001 From: Anton Kuklin Date: Sat, 2 May 2020 21:21:51 +0300 Subject: [PATCH] cmd: fail go mod vendor on case-insensitive import collisions The existing implementation of go mod vendor allows having case-insensitive imports, which will anyway fail during go build. This improvement validates such collisions during go mod vendor command. Fixes #38571 --- src/cmd/go/internal/modcmd/vendor.go | 19 +++++++++++ src/cmd/go/internal/str/str.go | 2 +- .../script/mod_vendor_case_insensitive.txt | 33 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/cmd/go/testdata/script/mod_vendor_case_insensitive.txt diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go index 1bc4ab3defea0..c718f5edec309 100644 --- a/src/cmd/go/internal/modcmd/vendor.go +++ b/src/cmd/go/internal/modcmd/vendor.go @@ -19,6 +19,7 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/imports" "cmd/go/internal/modload" + "cmd/go/internal/str" "golang.org/x/mod/module" "golang.org/x/mod/semver" @@ -64,6 +65,8 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { } _, pkgs := modload.LoadPackages(ctx, loadOpts, "all") + checkCaseInsensitivePathsDuplications(modload.LoadBuildList()) + vdir := filepath.Join(modload.ModRoot(), "vendor") if err := os.RemoveAll(vdir); err != nil { base.Fatalf("go mod vendor: %v", err) @@ -298,3 +301,19 @@ func copyDir(dst, src string, match func(dir string, info os.FileInfo) bool) { } } } + +// checkCaseInsensitivePathsDuplications verifies that given paths have +// no case-insensitive duplications. +// +// (See https://golang.org/issue/38571). +func checkCaseInsensitivePathsDuplications(mods []module.Version) { + paths := make(map[string]string, len(mods)) + for _, mod := range mods { + fold := str.ToFold(mod.Path) + if other := paths[fold]; other == "" { + paths[fold] = mod.Path + } else if other != mod.Path { + base.Fatalf("go mod vendor: case-insensitive module path collision: %s and %s", other, mod.Path) + } + } +} diff --git a/src/cmd/go/internal/str/str.go b/src/cmd/go/internal/str/str.go index 0413ed8e69e19..95747de6df071 100644 --- a/src/cmd/go/internal/str/str.go +++ b/src/cmd/go/internal/str/str.go @@ -30,7 +30,7 @@ func StringList(args ...interface{}) []string { } // ToFold returns a string with the property that -// strings.EqualFold(s, t) iff ToFold(s) == ToFold(t) +// strings.EqualFold(s, t) if ToFold(s) == ToFold(t) // This lets us test a large set of strings for fold-equivalent // duplicates without making a quadratic number of calls // to EqualFold. Note that strings.ToUpper and strings.ToLower diff --git a/src/cmd/go/testdata/script/mod_vendor_case_insensitive.txt b/src/cmd/go/testdata/script/mod_vendor_case_insensitive.txt new file mode 100644 index 0000000000000..b54ca9732ba13 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_vendor_case_insensitive.txt @@ -0,0 +1,33 @@ +# Tests issue #38571 +# Tests that go mod vendor fails on case-insensitive import collision. + +env GO111MODULE=on + +! go mod vendor +stderr 'case-insensitive module path collision' + +-- go.mod -- +module m + +go 1.14 + +require ( + example.com/foo v0.1.0 + example.com/Foo v0.1.0 +) + +replace ( + example.com/foo => ./foo + example.com/Foo => ./!foo +) +-- foo/go.mod -- +module example.com/foo + +-- foo/foo.go -- +package foo + +-- !foo/go.mod -- +module example.com/Foo + +-- !foo/foo.go -- +package Foo