From 7a095c32366593f637a95bc927c63454125e3015 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Tue, 15 Sep 2020 15:14:55 -0400 Subject: [PATCH] cmd/go/internal/modload: avoid a network fetch when querying a valid semantic version Test this behavior incidentally in a test for ambiguous import errors. (I rediscovered the error when writing the new test.) For #32567 Updates #28806 Change-Id: I323f05145734e5cf99818b9f04d65075f7c0f787 Reviewed-on: https://go-review.googlesource.com/c/go/+/255046 Trust: Bryan C. Mills Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot Reviewed-by: Michael Matloob Reviewed-by: Jay Conrod --- src/cmd/go/internal/modload/query.go | 21 +++++-- .../script/mod_get_ambiguous_import.txt | 60 +++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/cmd/go/testdata/script/mod_get_ambiguous_import.txt diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go index f67a738677..5ddb4e6565 100644 --- a/src/cmd/go/internal/modload/query.go +++ b/src/cmd/go/internal/modload/query.go @@ -212,7 +212,20 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed default: // Direct lookup of semantic version or commit identifier. - // + + // If the query is a valid semantic version and that version is replaced, + // use the replacement module without searching the proxy. + canonicalQuery := module.CanonicalVersion(query) + if canonicalQuery != "" { + m := module.Version{Path: path, Version: query} + if r := Replacement(m); r.Path != "" { + if err := allowed(ctx, m); errors.Is(err, ErrDisallowed) { + return nil, err + } + return &modfetch.RevInfo{Version: query}, nil + } + } + // If the identifier is not a canonical semver tag — including if it's a // semver tag with a +metadata suffix — then modfetch.Stat will populate // info.Version with a suitable pseudo-version. @@ -222,9 +235,9 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed // The full query doesn't correspond to a tag. If it is a semantic version // with a +metadata suffix, see if there is a tag without that suffix: // semantic versioning defines them to be equivalent. - if vers := module.CanonicalVersion(query); vers != "" && vers != query { - info, err = modfetch.Stat(proxy, path, vers) - if !errors.Is(err, os.ErrNotExist) { + if canonicalQuery != "" && query != canonicalQuery { + info, err = modfetch.Stat(proxy, path, canonicalQuery) + if err != nil && !errors.Is(err, os.ErrNotExist) { return info, err } } diff --git a/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt b/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt new file mode 100644 index 0000000000..8f5bf20636 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt @@ -0,0 +1,60 @@ +go list -m all +stdout '^example.net/m v0.1.0 ' +! stdout '^example.net/m/p ' +cp go.mod go.mod.orig + +# Upgrading example.net/m/p without also upgrading example.net/m +# causes the import of package example.net/m/p to be ambiguous. +# +# TODO(#27899): Should we automatically upgrade example.net/m to v0.2.0 +# to resolve the conflict? +! go get -d example.net/m/p@v1.0.0 +stderr '^go get example.net/m/p@v1.0.0: ambiguous import: found package example.net/m/p in multiple modules:\n\texample.net/m v0.1.0 \(.*[/\\]m1[/\\]p\)\n\texample.net/m/p v1.0.0 \(.*[/\\]p0\)\n\z' +cmp go.mod go.mod.orig + +# Upgrading both modules simultaneously resolves the ambiguous upgrade. +# Note that this command line mixes a module path (example.net/m) +# and a package path (example.net/m/p) in the same command. +go get -d example.net/m@v0.2.0 example.net/m/p@v1.0.0 + +go list -m all +stdout '^example.net/m v0.2.0 ' +stdout '^example.net/m/p v1.0.0 ' + +-- go.mod -- +module example.net/importer + +go 1.16 + +require ( + example.net/m v0.1.0 +) + +replace ( + example.net/m v0.1.0 => ./m1 + example.net/m v0.2.0 => ./m2 + example.net/m/p v1.0.0 => ./p0 +) +-- importer.go -- +package importer +import _ "example.net/m/p" +-- m1/go.mod -- +module example.net/m + +go 1.16 +-- m1/p/p.go -- +package p +-- m2/go.mod -- +module example.net/m + +go 1.16 +-- m2/README.txt -- +Package p has been moved to module …/m/p. +Module …/m/p does not require any version of module …/m. + +-- p0/go.mod -- +module example.net/m/p + +go 1.16 +-- p0/p.go -- +package p -- 2.50.0