summaryrefslogtreecommitdiff
path: root/libgo/go/cmd/go/internal/modload/query.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/cmd/go/internal/modload/query.go')
-rw-r--r--libgo/go/cmd/go/internal/modload/query.go181
1 files changed, 147 insertions, 34 deletions
diff --git a/libgo/go/cmd/go/internal/modload/query.go b/libgo/go/cmd/go/internal/modload/query.go
index 602bf47275d..53278b91002 100644
--- a/libgo/go/cmd/go/internal/modload/query.go
+++ b/libgo/go/cmd/go/internal/modload/query.go
@@ -9,15 +9,18 @@ import (
"fmt"
"os"
pathpkg "path"
+ "path/filepath"
"strings"
"sync"
+ "cmd/go/internal/cfg"
"cmd/go/internal/imports"
"cmd/go/internal/modfetch"
- "cmd/go/internal/module"
"cmd/go/internal/search"
- "cmd/go/internal/semver"
"cmd/go/internal/str"
+
+ "golang.org/x/mod/module"
+ "golang.org/x/mod/semver"
)
// Query looks up a revision of a given module given a version query string.
@@ -61,10 +64,24 @@ func Query(path, query, current string, allowed func(module.Version) bool) (*mod
return info, err
}
+var errQueryDisabled error = queryDisabledError{}
+
+type queryDisabledError struct{}
+
+func (queryDisabledError) Error() string {
+ if cfg.BuildModReason == "" {
+ return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod)
+ }
+ return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
+}
+
func queryProxy(proxy, path, query, current string, allowed func(module.Version) bool) (*modfetch.RevInfo, error) {
if current != "" && !semver.IsValid(current) {
return nil, fmt.Errorf("invalid previous version %q", current)
}
+ if cfg.BuildMod != "" && cfg.BuildMod != "mod" {
+ return nil, errQueryDisabled
+ }
if allowed == nil {
allowed = func(module.Version) bool { return true }
}
@@ -74,10 +91,20 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
badVersion := func(v string) (*modfetch.RevInfo, error) {
return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query)
}
- var ok func(module.Version) bool
- var prefix string
- var preferOlder bool
- var mayUseLatest bool
+ matchesMajor := func(v string) bool {
+ _, pathMajor, ok := module.SplitPathVersion(path)
+ if !ok {
+ return false
+ }
+ return module.CheckPathMajor(v, pathMajor) == nil
+ }
+ var (
+ ok func(module.Version) bool
+ prefix string
+ preferOlder bool
+ mayUseLatest bool
+ preferIncompatible bool = strings.HasSuffix(current, "+incompatible")
+ )
switch {
case query == "latest":
ok = allowed
@@ -110,6 +137,9 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
ok = func(m module.Version) bool {
return semver.Compare(m.Version, v) <= 0 && allowed(m)
}
+ if !matchesMajor(v) {
+ preferIncompatible = true
+ }
case strings.HasPrefix(query, "<"):
v := query[len("<"):]
@@ -119,6 +149,9 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
ok = func(m module.Version) bool {
return semver.Compare(m.Version, v) < 0 && allowed(m)
}
+ if !matchesMajor(v) {
+ preferIncompatible = true
+ }
case strings.HasPrefix(query, ">="):
v := query[len(">="):]
@@ -129,6 +162,9 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
return semver.Compare(m.Version, v) >= 0 && allowed(m)
}
preferOlder = true
+ if !matchesMajor(v) {
+ preferIncompatible = true
+ }
case strings.HasPrefix(query, ">"):
v := query[len(">"):]
@@ -143,12 +179,18 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
return semver.Compare(m.Version, v) > 0 && allowed(m)
}
preferOlder = true
+ if !matchesMajor(v) {
+ preferIncompatible = true
+ }
case semver.IsValid(query) && isSemverPrefix(query):
ok = func(m module.Version) bool {
return matchSemverPrefix(query, m.Version) && allowed(m)
}
prefix = query + "."
+ if !matchesMajor(query) {
+ preferIncompatible = true
+ }
default:
// Direct lookup of semantic version or commit identifier.
@@ -201,6 +243,10 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
if err != nil {
return nil, err
}
+ releases, prereleases, err := filterVersions(path, versions, ok, preferIncompatible)
+ if err != nil {
+ return nil, err
+ }
lookup := func(v string) (*modfetch.RevInfo, error) {
rev, err := repo.Stat(v)
@@ -221,28 +267,18 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
}
if preferOlder {
- for _, v := range versions {
- if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) {
- return lookup(v)
- }
+ if len(releases) > 0 {
+ return lookup(releases[0])
}
- for _, v := range versions {
- if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) {
- return lookup(v)
- }
+ if len(prereleases) > 0 {
+ return lookup(prereleases[0])
}
} else {
- for i := len(versions) - 1; i >= 0; i-- {
- v := versions[i]
- if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) {
- return lookup(v)
- }
+ if len(releases) > 0 {
+ return lookup(releases[len(releases)-1])
}
- for i := len(versions) - 1; i >= 0; i-- {
- v := versions[i]
- if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) {
- return lookup(v)
- }
+ if len(prereleases) > 0 {
+ return lookup(prereleases[len(prereleases)-1])
}
}
@@ -286,6 +322,52 @@ func matchSemverPrefix(p, v string) bool {
return len(v) > len(p) && v[len(p)] == '.' && v[:len(p)] == p && semver.Prerelease(v) == ""
}
+// filterVersions classifies versions into releases and pre-releases, filtering
+// out:
+// 1. versions that do not satisfy the 'ok' predicate, and
+// 2. "+incompatible" versions, if a compatible one satisfies the predicate
+// and the incompatible version is not preferred.
+func filterVersions(path string, versions []string, ok func(module.Version) bool, preferIncompatible bool) (releases, prereleases []string, err error) {
+ var lastCompatible string
+ for _, v := range versions {
+ if !ok(module.Version{Path: path, Version: v}) {
+ continue
+ }
+
+ if !preferIncompatible {
+ if !strings.HasSuffix(v, "+incompatible") {
+ lastCompatible = v
+ } else if lastCompatible != "" {
+ // If the latest compatible version is allowed and has a go.mod file,
+ // ignore any version with a higher (+incompatible) major version. (See
+ // https://golang.org/issue/34165.) Note that we even prefer a
+ // compatible pre-release over an incompatible release.
+
+ ok, err := versionHasGoMod(module.Version{Path: path, Version: lastCompatible})
+ if err != nil {
+ return nil, nil, err
+ }
+ if ok {
+ break
+ }
+
+ // No acceptable compatible release has a go.mod file, so the versioning
+ // for the module might not be module-aware, and we should respect
+ // legacy major-version tags.
+ preferIncompatible = true
+ }
+ }
+
+ if semver.Prerelease(v) != "" {
+ prereleases = append(prereleases, v)
+ } else {
+ releases = append(releases, v)
+ }
+ }
+
+ return releases, prereleases, nil
+}
+
type QueryResult struct {
Mod module.Version
Rev *modfetch.RevInfo
@@ -381,9 +463,10 @@ func QueryPattern(pattern, query string, allowed func(module.Version) bool) ([]Q
r.Packages = match(r.Mod, root, isLocal)
if len(r.Packages) == 0 {
return r, &PackageNotInModuleError{
- Mod: r.Mod,
- Query: query,
- Pattern: pattern,
+ Mod: r.Mod,
+ Replacement: Replacement(r.Mod),
+ Query: query,
+ Pattern: pattern,
}
}
return r, nil
@@ -471,7 +554,17 @@ func queryPrefixModules(candidateModules []string, queryModule func(path string)
notExistErr = rErr
}
} else if err == nil {
- err = r.err
+ if len(found) > 0 || noPackage != nil {
+ // golang.org/issue/34094: If we have already found a module that
+ // could potentially contain the target package, ignore unclassified
+ // errors for modules with shorter paths.
+
+ // golang.org/issue/34383 is a special case of this: if we have
+ // already found example.com/foo/v2@v2.0.0 with a matching go.mod
+ // file, ignore the error from example.com/foo@v2.0.0.
+ } else {
+ err = r.err
+ }
}
}
}
@@ -526,21 +619,32 @@ func (e *NoMatchingVersionError) Error() string {
// code for the versions it knows about, and thus did not have the opportunity
// to return a non-400 status code to suppress fallback.
type PackageNotInModuleError struct {
- Mod module.Version
- Query string
- Pattern string
+ Mod module.Version
+ Replacement module.Version
+ Query string
+ Pattern string
}
func (e *PackageNotInModuleError) Error() string {
found := ""
- if e.Query != e.Mod.Version {
+ if r := e.Replacement; r.Path != "" {
+ replacement := r.Path
+ if r.Version != "" {
+ replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
+ }
+ if e.Query == e.Mod.Version {
+ found = fmt.Sprintf(" (replaced by %s)", replacement)
+ } else {
+ found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
+ }
+ } else if e.Query != e.Mod.Version {
found = fmt.Sprintf(" (%s)", e.Mod.Version)
}
if strings.Contains(e.Pattern, "...") {
- return fmt.Sprintf("module %s@%s%s found, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
+ return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
}
- return fmt.Sprintf("module %s@%s%s found, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
+ return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
}
// ModuleHasRootPackage returns whether module m contains a package m.Path.
@@ -552,3 +656,12 @@ func ModuleHasRootPackage(m module.Version) (bool, error) {
_, ok := dirInModule(m.Path, m.Path, root, isLocal)
return ok, nil
}
+
+func versionHasGoMod(m module.Version) (bool, error) {
+ root, _, err := fetch(m)
+ if err != nil {
+ return false, err
+ }
+ fi, err := os.Stat(filepath.Join(root, "go.mod"))
+ return err == nil && !fi.IsDir(), nil
+}