Commit 78a11c21 authored by Russ Cox's avatar Russ Cox

cmd/go: detect case-insensitive import path collision

We already detect this collision when both imports are used
anywhere in a single program. Also detect it when they are in
different targets being processed together.

Fixes #20264.

Change-Id: I5d3c822aae136053fbcb5ed167e1d67f9b847a0f
Reviewed-on: https://go-review.googlesource.com/46424
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent e00a38c8
...@@ -2040,8 +2040,10 @@ func TestCaseCollisions(t *testing.T) { ...@@ -2040,8 +2040,10 @@ func TestCaseCollisions(t *testing.T) {
)`) )`)
tg.tempFile("src/example/a/pkg/pkg.go", `package pkg`) tg.tempFile("src/example/a/pkg/pkg.go", `package pkg`)
tg.tempFile("src/example/a/Pkg/pkg.go", `package pkg`) tg.tempFile("src/example/a/Pkg/pkg.go", `package pkg`)
tg.runFail("list", "example/a") tg.run("list", "-json", "example/a")
tg.grepStderr("case-insensitive import collision", "go list example/a did not report import collision") tg.grepStdout("case-insensitive import collision", "go list -json example/a did not report import collision")
tg.runFail("build", "example/a")
tg.grepStderr("case-insensitive import collision", "go build example/a did not report import collision")
tg.tempFile("src/example/b/file.go", `package b`) tg.tempFile("src/example/b/file.go", `package b`)
tg.tempFile("src/example/b/FILE.go", `package b`) tg.tempFile("src/example/b/FILE.go", `package b`)
f, err := os.Open(tg.path("src/example/b")) f, err := os.Open(tg.path("src/example/b"))
...@@ -2059,6 +2061,13 @@ func TestCaseCollisions(t *testing.T) { ...@@ -2059,6 +2061,13 @@ func TestCaseCollisions(t *testing.T) {
} }
tg.runFail(args...) tg.runFail(args...)
tg.grepStderr("case-insensitive file name collision", "go list example/b did not report file name collision") tg.grepStderr("case-insensitive file name collision", "go list example/b did not report file name collision")
tg.runFail("list", "example/a/pkg", "example/a/Pkg")
tg.grepStderr("case-insensitive import collision", "go list example/a/pkg example/a/Pkg did not report import collision")
tg.run("list", "-json", "-e", "example/a/pkg", "example/a/Pkg")
tg.grepStdout("case-insensitive import collision", "go list -json -e example/a/pkg example/a/Pkg did not report import collision")
tg.runFail("build", "example/a/pkg", "example/a/Pkg")
tg.grepStderr("case-insensitive import collision", "go build example/a/pkg example/a/Pkg did not report import collision")
} }
// Issue 8181. // Issue 8181.
......
...@@ -828,6 +828,8 @@ var cgoSyscallExclude = map[string]bool{ ...@@ -828,6 +828,8 @@ var cgoSyscallExclude = map[string]bool{
"runtime/msan": true, "runtime/msan": true,
} }
var foldPath = make(map[string]string)
// load populates p using information from bp, err, which should // load populates p using information from bp, err, which should
// be the result of calling build.Context.Import. // be the result of calling build.Context.Import.
func (p *Package) load(stk *ImportStack, bp *build.Package, err error) *Package { func (p *Package) load(stk *ImportStack, bp *build.Package, err error) *Package {
...@@ -1109,17 +1111,16 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) *Package ...@@ -1109,17 +1111,16 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) *Package
return p return p
} }
// In the absence of errors lower in the dependency tree, // Check for case-insensitive collisions of import paths.
// check for case-insensitive collisions of import paths. fold := str.ToFold(p.ImportPath)
if len(p.DepsErrors) == 0 { if other := foldPath[fold]; other == "" {
dep1, dep2 := str.FoldDup(p.Deps) foldPath[fold] = p.ImportPath
if dep1 != "" { } else if other != p.ImportPath {
p.Error = &PackageError{ p.Error = &PackageError{
ImportStack: stk.Copy(), ImportStack: stk.Copy(),
Err: fmt.Sprintf("case-insensitive import collision: %q and %q", dep1, dep2), Err: fmt.Sprintf("case-insensitive import collision: %q and %q", p.ImportPath, other),
}
return p
} }
return p
} }
if p.BinaryOnly { if p.BinaryOnly {
......
...@@ -29,13 +29,13 @@ func StringList(args ...interface{}) []string { ...@@ -29,13 +29,13 @@ func StringList(args ...interface{}) []string {
return x return x
} }
// toFold returns a string with the property that // ToFold returns a string with the property that
// strings.EqualFold(s, t) iff toFold(s) == toFold(t) // strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
// This lets us test a large set of strings for fold-equivalent // This lets us test a large set of strings for fold-equivalent
// duplicates without making a quadratic number of calls // duplicates without making a quadratic number of calls
// to EqualFold. Note that strings.ToUpper and strings.ToLower // to EqualFold. Note that strings.ToUpper and strings.ToLower
// do not have the desired property in some corner cases. // do not have the desired property in some corner cases.
func toFold(s string) string { func ToFold(s string) string {
// Fast path: all ASCII, no upper case. // Fast path: all ASCII, no upper case.
// Most paths look like this already. // Most paths look like this already.
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
...@@ -74,7 +74,7 @@ Slow: ...@@ -74,7 +74,7 @@ Slow:
func FoldDup(list []string) (string, string) { func FoldDup(list []string) (string, string) {
clash := map[string]string{} clash := map[string]string{}
for _, s := range list { for _, s := range list {
fold := toFold(s) fold := ToFold(s)
if t := clash[fold]; t != "" { if t := clash[fold]; t != "" {
if s > t { if s > t {
s, t = t, s s, t = t, s
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment