From 68170aa677b9fc70ce3d0fc152354e0a7e63c8cf Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Thu, 26 Jul 2018 16:42:15 -0400 Subject: [PATCH] cmd/go: forbid use of one module with two different paths If a single module is imported via two different paths (e.g., as itself and as a replacement for something else), some users may be surprised if the two paths do not share the same package-level state. Others may be surprised if the two paths do share state. Punt on the question for now by rejecting that condition explicitly. Fixes #26607. Change-Id: I15c3889f61f8dd4ba5e5c48ca33ad63aeecac04e Reviewed-on: https://go-review.googlesource.com/126156 Run-TryBot: Bryan C. Mills TryBot-Result: Gobot Gobot Reviewed-by: Russ Cox --- src/cmd/go/internal/modload/load.go | 19 +++++ src/cmd/go/testdata/script/mod_replace.txt | 89 ++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/cmd/go/testdata/script/mod_replace.txt diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index 36dc0deee7818..e8c984baa79e3 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -115,6 +115,25 @@ func ImportPaths(args []string) []string { } return roots }) + + // A given module path may be used as itself or as a replacement for another + // module, but not both at the same time. Otherwise, the aliasing behavior is + // too subtle (see https://golang.org/issue/26607), and we don't want to + // commit to a specific behavior at this point. + firstPath := make(map[module.Version]string, len(buildList)) + for _, mod := range buildList { + src := mod + if rep := Replacement(mod); rep.Path != "" { + src = rep + } + if prev, ok := firstPath[src]; !ok { + firstPath[src] = mod.Path + } else if prev != mod.Path { + base.Errorf("go: %s@%s used for two different module paths (%s and %s)", mod.Path, mod.Version, prev, mod.Path) + } + } + base.ExitIfErrors() + WriteGoMod() // Process paths to produce final paths list. diff --git a/src/cmd/go/testdata/script/mod_replace.txt b/src/cmd/go/testdata/script/mod_replace.txt new file mode 100644 index 0000000000000..3e7f0bc313eb6 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_replace.txt @@ -0,0 +1,89 @@ +env GO111MODULE=on + +go build -o a1.exe . +exec ./a1.exe +stdout 'Don''t communicate by sharing memory' + +# Modules can be replaced by local packages. +go mod -replace=rsc.io/quote/v3=./local/rsc.io/quote/v3 +go build -o a2.exe . +exec ./a2.exe +stdout 'Concurrency is not parallelism.' + +# The module path of the replacement doesn't need to match. +# (For example, it could be a long-running fork with its own import path.) +go mod -replace=rsc.io/quote/v3=./local/not-rsc.io/quote/v3 +go build -o a3.exe . +exec ./a3.exe +stdout 'Clear is better than clever.' + +# However, the same module can't be used as two different paths. +go mod -dropreplace=rsc.io/quote/v3 +go mod -replace=not-rsc.io/quote/v3@v3.0.0=rsc.io/quote/v3@v3.0.0 +go mod -require=not-rsc.io/quote/v3@v3.0.0 +! go build -o a4.exe . + + +-- go.mod -- +module quoter + +require rsc.io/quote/v3 v3.0.0 + +-- main.go -- +package main + +import ( + "fmt" + "rsc.io/quote/v3" +) + +func main() { + fmt.Println(quote.GoV3()) +} + +-- local/rsc.io/quote/v3/go.mod -- +module rsc.io/quote/v3 + +require rsc.io/sampler v1.3.0 + +-- local/rsc.io/quote/v3/quote.go -- +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package quote collects pithy sayings. +package quote + +import "rsc.io/sampler" + +// Hello returns a greeting. +func HelloV3() string { + return sampler.Hello() +} + +// Glass returns a useful phrase for world travelers. +func GlassV3() string { + // See http://www.oocities.org/nodotus/hbglass.html. + return "I can eat glass and it doesn't hurt me." +} + +// Go returns a REPLACED Go proverb. +func GoV3() string { + return "Concurrency is not parallelism." +} + +// Opt returns a optimization truth. +func OptV3() string { + // Wisdom from ken. + return "If a program is too slow, it must have a loop." +} + +-- local/not-rsc.io/quote/v3/go.mod -- +module not-rsc.io/quote/v3 + +-- local/not-rsc.io/quote/v3/quote.go -- +package quote + +func GoV3() string { + return "Clear is better than clever." +}