1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
22 "cmd/go/internal/gover"
23 "cmd/go/internal/imports"
24 "cmd/go/internal/modfetch"
25 "cmd/go/internal/modfetch/codehost"
26 "cmd/go/internal/modinfo"
27 "cmd/go/internal/search"
29 "cmd/go/internal/trace"
30 "cmd/internal/pkgpattern"
32 "golang.org/x/mod/module"
33 "golang.org/x/mod/semver"
36 // Query looks up a revision of a given module given a version query string.
37 // The module must be a complete module path.
38 // The version must take one of the following forms:
40 // - the literal string "latest", denoting the latest available, allowed
41 // tagged version, with non-prereleases preferred over prereleases.
42 // If there are no tagged versions in the repo, latest returns the most
45 // - the literal string "upgrade", equivalent to "latest" except that if
46 // current is a newer version, current will be returned (see below).
48 // - the literal string "patch", denoting the latest available tagged version
49 // with the same major and minor number as current (see below).
51 // - v1, denoting the latest available tagged version v1.x.x.
53 // - v1.2, denoting the latest available tagged version v1.2.x.
55 // - v1.2.3, a semantic version string denoting that tagged version.
57 // - <v1.2.3, <=v1.2.3, >v1.2.3, >=v1.2.3,
58 // denoting the version closest to the target and satisfying the given operator,
59 // with non-prereleases preferred over prereleases.
61 // - a repository commit identifier or tag, denoting that commit.
63 // current denotes the currently-selected version of the module; it may be
64 // "none" if no version is currently selected, or "" if the currently-selected
65 // version is unknown or should not be considered. If query is
66 // "upgrade" or "patch", current will be returned if it is a newer
67 // semantic version or a chronologically later pseudo-version than the
68 // version that would otherwise be chosen. This prevents accidental downgrades
69 // from newer pre-release or development versions.
71 // The allowed function (which may be nil) is used to filter out unsuitable
72 // versions (see AllowedFunc documentation for details). If the query refers to
73 // a specific revision (for example, "master"; see IsRevisionQuery), and the
74 // revision is disallowed by allowed, Query returns the error. If the query
75 // does not refer to a specific revision (for example, "latest"), Query
76 // acts as if versions disallowed by allowed do not exist.
78 // If path is the path of the main module and the query is "latest",
79 // Query returns Target.Version as the version.
81 // Query often returns a non-nil *RevInfo with a non-nil error,
82 // to provide an info.Origin that can allow the error to be cached.
83 func Query(ctx context.Context, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) {
84 ctx, span := trace.StartSpan(ctx, "modload.Query "+path)
87 return queryReuse(ctx, path, query, current, allowed, nil)
90 // queryReuse is like Query but also takes a map of module info that can be reused
91 // if the validation criteria in Origin are met.
92 func queryReuse(ctx context.Context, path, query, current string, allowed AllowedFunc, reuse map[module.Version]*modinfo.ModulePublic) (*modfetch.RevInfo, error) {
93 var info *modfetch.RevInfo
94 err := modfetch.TryProxies(func(proxy string) (err error) {
95 info, err = queryProxy(ctx, proxy, path, query, current, allowed, reuse)
101 // checkReuse checks whether a revision of a given module or a version list
102 // for a given module may be reused, according to the information in origin.
103 func checkReuse(ctx context.Context, path string, old *codehost.Origin) error {
104 return modfetch.TryProxies(func(proxy string) error {
105 repo, err := lookupRepo(ctx, proxy, path)
109 return repo.CheckReuse(ctx, old)
113 // AllowedFunc is used by Query and other functions to filter out unsuitable
114 // versions, for example, those listed in exclude directives in the main
115 // module's go.mod file.
117 // An AllowedFunc returns an error equivalent to ErrDisallowed for an unsuitable
118 // version. Any other error indicates the function was unable to determine
119 // whether the version should be allowed, for example, the function was unable
120 // to fetch or parse a go.mod file containing retractions. Typically, errors
121 // other than ErrDisallowed may be ignored.
122 type AllowedFunc func(context.Context, module.Version) error
124 var errQueryDisabled error = queryDisabledError{}
126 type queryDisabledError struct{}
128 func (queryDisabledError) Error() string {
129 if cfg.BuildModReason == "" {
130 return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod)
132 return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
135 func queryProxy(ctx context.Context, proxy, path, query, current string, allowed AllowedFunc, reuse map[module.Version]*modinfo.ModulePublic) (*modfetch.RevInfo, error) {
136 ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query)
139 if current != "" && current != "none" && !gover.ModIsValid(path, current) {
140 return nil, fmt.Errorf("invalid previous version %v@%v", path, current)
142 if cfg.BuildMod == "vendor" {
143 return nil, errQueryDisabled
146 allowed = func(context.Context, module.Version) error { return nil }
149 if MainModules.Contains(path) && (query == "upgrade" || query == "patch") {
150 m := module.Version{Path: path}
151 if err := allowed(ctx, m); err != nil {
152 return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err)
154 return &modfetch.RevInfo{Version: m.Version}, nil
157 if path == "std" || path == "cmd" {
158 return nil, fmt.Errorf("can't query specific version (%q) of standard-library module %q", query, path)
161 repo, err := lookupRepo(ctx, proxy, path)
166 if old := reuse[module.Version{Path: path, Version: query}]; old != nil {
167 if err := repo.CheckReuse(ctx, old.Origin); err == nil {
168 info := &modfetch.RevInfo{
169 Version: old.Version,
173 info.Time = *old.Time
179 // Parse query to detect parse errors (and possibly handle query)
180 // before any network I/O.
181 qm, err := newQueryMatcher(path, query, current, allowed)
182 if (err == nil && qm.canStat) || err == errRevQuery {
183 // Direct lookup of a commit identifier or complete (non-prefix) semantic
186 // If the identifier is not a canonical semver tag — including if it's a
187 // semver tag with a +metadata suffix — then modfetch.Stat will populate
188 // info.Version with a suitable pseudo-version.
189 info, err := repo.Stat(ctx, query)
192 // The full query doesn't correspond to a tag. If it is a semantic version
193 // with a +metadata suffix, see if there is a tag without that suffix:
194 // semantic versioning defines them to be equivalent.
195 canonicalQuery := module.CanonicalVersion(query)
196 if canonicalQuery != "" && query != canonicalQuery {
197 info, err = repo.Stat(ctx, canonicalQuery)
198 if err != nil && !errors.Is(err, fs.ErrNotExist) {
203 return info, queryErr
206 if err := allowed(ctx, module.Version{Path: path, Version: info.Version}); errors.Is(err, ErrDisallowed) {
210 } else if err != nil {
214 // Load versions and execute query.
215 versions, err := repo.Versions(ctx, qm.prefix)
219 revErr := &modfetch.RevInfo{Origin: versions.Origin} // RevInfo to return with error
221 releases, prereleases, err := qm.filterVersions(ctx, versions.List)
226 mergeRevOrigin := func(rev *modfetch.RevInfo, origin *codehost.Origin) *modfetch.RevInfo {
227 merged := mergeOrigin(rev.Origin, origin)
228 if merged == rev.Origin {
231 clone := new(modfetch.RevInfo)
233 clone.Origin = merged
237 lookup := func(v string) (*modfetch.RevInfo, error) {
238 rev, err := repo.Stat(ctx, v)
239 // Stat can return a non-nil rev and a non-nil err,
240 // in order to provide origin information to make the error cacheable.
241 if rev == nil && err != nil {
244 rev = mergeRevOrigin(rev, versions.Origin)
249 if (query == "upgrade" || query == "patch") && module.IsPseudoVersion(current) && !rev.Time.IsZero() {
250 // Don't allow "upgrade" or "patch" to move from a pseudo-version
251 // to a chronologically older version or pseudo-version.
253 // If the current version is a pseudo-version from an untagged branch, it
254 // may be semantically lower than the "latest" release or the latest
255 // pseudo-version on the main branch. A user on such a version is unlikely
256 // to intend to “upgrade” to a version that already existed at that point
259 // We do this only if the current version is a pseudo-version: if the
260 // version is tagged, the author of the dependency module has given us
261 // explicit information about their intended precedence of this version
262 // relative to other versions, and we shouldn't contradict that
263 // information. (For example, v1.0.1 might be a backport of a fix already
264 // incorporated into v1.1.0, in which case v1.0.1 would be chronologically
265 // newer but v1.1.0 is still an “upgrade”; or v1.0.2 might be a revert of
266 // an unsuccessful fix in v1.0.1, in which case the v1.0.2 commit may be
267 // older than the v1.0.1 commit despite the tag itself being newer.)
268 currentTime, err := module.PseudoVersionTime(current)
269 if err == nil && rev.Time.Before(currentTime) {
270 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
273 rev, err = repo.Stat(ctx, current)
274 if rev == nil && err != nil {
277 rev = mergeRevOrigin(rev, versions.Origin)
286 if len(releases) > 0 {
287 return lookup(releases[0])
289 if len(prereleases) > 0 {
290 return lookup(prereleases[0])
293 if len(releases) > 0 {
294 return lookup(releases[len(releases)-1])
296 if len(prereleases) > 0 {
297 return lookup(prereleases[len(prereleases)-1])
302 latest, err := repo.Latest(ctx)
304 if qm.allowsVersion(ctx, latest.Version) {
305 return lookup(latest.Version)
307 } else if !errors.Is(err, fs.ErrNotExist) {
312 if (query == "upgrade" || query == "patch") && current != "" && current != "none" {
313 // "upgrade" and "patch" may stay on the current version if allowed.
314 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
317 return lookup(current)
320 return revErr, &NoMatchingVersionError{query: query, current: current}
323 // IsRevisionQuery returns true if vers is a version query that may refer to
324 // a particular version or revision in a repository like "v1.0.0", "master",
325 // or "0123abcd". IsRevisionQuery returns false if vers is a query that
326 // chooses from among available versions like "latest" or ">v1.0.0".
327 func IsRevisionQuery(path, vers string) bool {
328 if vers == "latest" ||
331 strings.HasPrefix(vers, "<") ||
332 strings.HasPrefix(vers, ">") ||
333 (gover.ModIsValid(path, vers) && gover.ModIsPrefix(path, vers)) {
339 type queryMatcher struct {
342 filter func(version string) bool
344 canStat bool // if true, the query can be resolved by repo.Stat
345 preferLower bool // if true, choose the lowest matching version
347 preferIncompatible bool
350 var errRevQuery = errors.New("query refers to a non-semver revision")
352 // newQueryMatcher returns a new queryMatcher that matches the versions
353 // specified by the given query on the module with the given path.
355 // If the query can only be resolved by statting a non-SemVer revision,
356 // newQueryMatcher returns errRevQuery.
357 func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (*queryMatcher, error) {
358 badVersion := func(v string) (*queryMatcher, error) {
359 return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query)
362 matchesMajor := func(v string) bool {
363 _, pathMajor, ok := module.SplitPathVersion(path)
367 return module.CheckPathMajor(v, pathMajor) == nil
373 preferIncompatible: strings.HasSuffix(current, "+incompatible"),
377 case query == "latest":
378 qm.mayUseLatest = true
380 case query == "upgrade":
381 if current == "" || current == "none" {
382 qm.mayUseLatest = true
384 qm.mayUseLatest = module.IsPseudoVersion(current)
385 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, current) >= 0 }
388 case query == "patch":
389 if current == "" || current == "none" {
390 return nil, &NoPatchBaseError{path}
393 qm.mayUseLatest = true
395 qm.mayUseLatest = module.IsPseudoVersion(current)
396 qm.prefix = gover.ModMajorMinor(qm.path, current) + "."
397 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, current) >= 0 }
400 case strings.HasPrefix(query, "<="):
401 v := query[len("<="):]
402 if !gover.ModIsValid(path, v) {
405 if gover.ModIsPrefix(path, v) {
406 // Refuse to say whether <=v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3).
407 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
409 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) <= 0 }
410 if !matchesMajor(v) {
411 qm.preferIncompatible = true
414 case strings.HasPrefix(query, "<"):
415 v := query[len("<"):]
416 if !gover.ModIsValid(path, v) {
419 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) < 0 }
420 if !matchesMajor(v) {
421 qm.preferIncompatible = true
424 case strings.HasPrefix(query, ">="):
425 v := query[len(">="):]
426 if !gover.ModIsValid(path, v) {
429 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) >= 0 }
430 qm.preferLower = true
431 if !matchesMajor(v) {
432 qm.preferIncompatible = true
435 case strings.HasPrefix(query, ">"):
436 v := query[len(">"):]
437 if !gover.ModIsValid(path, v) {
440 if gover.ModIsPrefix(path, v) {
441 // Refuse to say whether >v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3).
442 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
444 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) > 0 }
445 qm.preferLower = true
446 if !matchesMajor(v) {
447 qm.preferIncompatible = true
450 case gover.ModIsValid(path, query):
451 if gover.ModIsPrefix(path, query) {
452 qm.prefix = query + "."
453 // Do not allow the query "v1.2" to match versions lower than "v1.2.0",
454 // such as prereleases for that version. (https://golang.org/issue/31972)
455 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, query) >= 0 }
458 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, query) == 0 }
459 qm.prefix = semver.Canonical(query)
461 if !matchesMajor(query) {
462 qm.preferIncompatible = true
466 return nil, errRevQuery
472 // allowsVersion reports whether version v is allowed by the prefix, filter, and
473 // AllowedFunc of qm.
474 func (qm *queryMatcher) allowsVersion(ctx context.Context, v string) bool {
475 if qm.prefix != "" && !strings.HasPrefix(v, qm.prefix) {
478 if qm.filter != nil && !qm.filter(v) {
481 if qm.allowed != nil {
482 if err := qm.allowed(ctx, module.Version{Path: qm.path, Version: v}); errors.Is(err, ErrDisallowed) {
489 // filterVersions classifies versions into releases and pre-releases, filtering
491 // 1. versions that do not satisfy the 'allowed' predicate, and
492 // 2. "+incompatible" versions, if a compatible one satisfies the predicate
493 // and the incompatible version is not preferred.
495 // If the allowed predicate returns an error not equivalent to ErrDisallowed,
496 // filterVersions returns that error.
497 func (qm *queryMatcher) filterVersions(ctx context.Context, versions []string) (releases, prereleases []string, err error) {
498 needIncompatible := qm.preferIncompatible
500 var lastCompatible string
501 for _, v := range versions {
502 if !qm.allowsVersion(ctx, v) {
506 if !needIncompatible {
507 // We're not yet sure whether we need to include +incompatible versions.
508 // Keep track of the last compatible version we've seen, and use the
509 // presence (or absence) of a go.mod file in that version to decide: a
510 // go.mod file implies that the module author is supporting modules at a
511 // compatible version (and we should ignore +incompatible versions unless
512 // requested explicitly), while a lack of go.mod file implies the
513 // potential for legacy (pre-modules) versioning without semantic import
514 // paths (and thus *with* +incompatible versions).
516 // This isn't strictly accurate if the latest compatible version has been
517 // replaced by a local file path, because we do not allow file-path
518 // replacements without a go.mod file: the user would have needed to add
519 // one. However, replacing the last compatible version while
520 // simultaneously expecting to upgrade implicitly to a +incompatible
521 // version seems like an extreme enough corner case to ignore for now.
523 if !strings.HasSuffix(v, "+incompatible") {
525 } else if lastCompatible != "" {
526 // If the latest compatible version is allowed and has a go.mod file,
527 // ignore any version with a higher (+incompatible) major version. (See
528 // https://golang.org/issue/34165.) Note that we even prefer a
529 // compatible pre-release over an incompatible release.
530 ok, err := versionHasGoMod(ctx, module.Version{Path: qm.path, Version: lastCompatible})
535 // The last compatible version has a go.mod file, so that's the
536 // highest version we're willing to consider. Don't bother even
537 // looking at higher versions, because they're all +incompatible from
542 // No acceptable compatible release has a go.mod file, so the versioning
543 // for the module might not be module-aware, and we should respect
544 // legacy major-version tags.
545 needIncompatible = true
549 if gover.ModIsPrerelease(qm.path, v) {
550 prereleases = append(prereleases, v)
552 releases = append(releases, v)
556 return releases, prereleases, nil
559 type QueryResult struct {
561 Rev *modfetch.RevInfo
565 // QueryPackages is like QueryPattern, but requires that the pattern match at
566 // least one package and omits the non-package result (if any).
567 func QueryPackages(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) {
568 pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)
570 if len(pkgMods) == 0 && err == nil {
571 replacement := Replacement(modOnly.Mod)
572 return nil, &PackageNotInModuleError{
574 Replacement: replacement,
583 // QueryPattern looks up the module(s) containing at least one package matching
584 // the given pattern at the given version. The results are sorted by module path
585 // length in descending order. If any proxy provides a non-empty set of candidate
586 // modules, no further proxies are tried.
588 // For wildcard patterns, QueryPattern looks in modules with package paths up to
589 // the first "..." in the pattern. For the pattern "example.com/a/b.../c",
590 // QueryPattern would consider prefixes of "example.com/a".
592 // If any matching package is in the main module, QueryPattern considers only
593 // the main module and only the version "latest", without checking for other
596 // QueryPattern always returns at least one QueryResult (which may be only
597 // modOnly) or a non-nil error.
598 func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) (pkgMods []QueryResult, modOnly *QueryResult, err error) {
599 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query)
604 firstError := func(m *search.Match) error {
605 if len(m.Errs) == 0 {
611 var match func(mod module.Version, roots []string, isLocal bool) *search.Match
612 matchPattern := pkgpattern.MatchPattern(pattern)
614 if i := strings.Index(pattern, "..."); i >= 0 {
615 base = pathpkg.Dir(pattern[:i+3])
617 return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query}
619 match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
620 m := search.NewMatch(pattern)
621 matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod})
625 match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
626 m := search.NewMatch(pattern)
628 if MainModules.Contains(mod.Path) {
629 prefix = MainModules.PathPrefix(module.Version{Path: mod.Path})
631 for _, root := range roots {
632 if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
635 m.Pkgs = []string{pattern}
642 var mainModuleMatches []module.Version
643 for _, mainModule := range MainModules.Versions() {
644 m := match(mainModule, modRoots, true)
646 if query != "upgrade" && query != "patch" {
647 return nil, nil, &QueryMatchesPackagesInMainModuleError{
653 if err := allowed(ctx, mainModule); err != nil {
654 return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, mainModule.Path, err)
656 return []QueryResult{{
658 Rev: &modfetch.RevInfo{Version: mainModule.Version},
662 if err := firstError(m); err != nil {
666 var matchesMainModule bool
667 if matchPattern(mainModule.Path) {
668 mainModuleMatches = append(mainModuleMatches, mainModule)
669 matchesMainModule = true
672 if (query == "upgrade" || query == "patch") && matchesMainModule {
673 if err := allowed(ctx, mainModule); err == nil {
674 modOnly = &QueryResult{
676 Rev: &modfetch.RevInfo{Version: mainModule.Version},
683 results []QueryResult
684 candidateModules = modulePrefixesExcludingTarget(base)
686 if len(candidateModules) == 0 {
688 return nil, modOnly, nil
689 } else if len(mainModuleMatches) != 0 {
690 return nil, nil, &QueryMatchesMainModulesError{
691 MainModules: mainModuleMatches,
696 return nil, nil, &PackageNotInModuleError{
697 MainModules: mainModuleMatches,
704 err = modfetch.TryProxies(func(proxy string) error {
705 queryModule := func(ctx context.Context, path string) (r QueryResult, err error) {
706 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path)
709 pathCurrent := current(path)
711 r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed, nil)
715 r.Mod.Version = r.Rev.Version
716 if gover.IsToolchain(r.Mod.Path) {
719 root, isLocal, err := fetch(ctx, r.Mod)
723 m := match(r.Mod, []string{root}, isLocal)
725 if len(r.Packages) == 0 && !matchPattern(path) {
726 if err := firstError(m); err != nil {
729 replacement := Replacement(r.Mod)
730 return r, &PackageNotInModuleError{
732 Replacement: replacement,
740 allResults, err := queryPrefixModules(ctx, candidateModules, queryModule)
741 results = allResults[:0]
742 for _, r := range allResults {
743 if len(r.Packages) == 0 {
746 results = append(results, r)
752 if len(mainModuleMatches) > 0 && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
753 return nil, nil, &QueryMatchesMainModulesError{
758 return slices.Clip(results), modOnly, err
761 // modulePrefixesExcludingTarget returns all prefixes of path that may plausibly
762 // exist as a module, excluding targetPrefix but otherwise including path
763 // itself, sorted by descending length. Prefixes that are not valid module paths
764 // but are valid package paths (like "m" or "example.com/.gen") are included,
765 // since they might be replaced.
766 func modulePrefixesExcludingTarget(path string) []string {
767 prefixes := make([]string, 0, strings.Count(path, "/")+1)
769 mainModulePrefixes := make(map[string]bool)
770 for _, m := range MainModules.Versions() {
771 mainModulePrefixes[m.Path] = true
775 if !mainModulePrefixes[path] {
776 if _, _, ok := module.SplitPathVersion(path); ok {
777 prefixes = append(prefixes, path)
781 j := strings.LastIndexByte(path, '/')
791 func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) {
792 ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules")
795 // If the path we're attempting is not in the module cache and we don't have a
796 // fetch result cached either, we'll end up making a (potentially slow)
797 // request to the proxy or (often even slower) the origin server.
798 // To minimize latency, execute all of those requests in parallel.
803 results := make([]result, len(candidateModules))
804 var wg sync.WaitGroup
805 wg.Add(len(candidateModules))
806 for i, p := range candidateModules {
807 ctx := trace.StartGoroutine(ctx)
808 go func(p string, r *result) {
809 r.QueryResult, r.err = queryModule(ctx, p)
815 // Classify the results. In case of failure, identify the error that the user
816 // is most likely to find helpful: the most useful class of error at the
817 // longest matching path.
819 noPackage *PackageNotInModuleError
820 noVersion *NoMatchingVersionError
821 noPatchBase *NoPatchBaseError
822 invalidPath *module.InvalidPathError // see comment in case below
825 for _, r := range results {
826 switch rErr := r.err.(type) {
828 found = append(found, r.QueryResult)
829 case *PackageNotInModuleError:
830 // Given the option, prefer to attribute “package not in module”
831 // to modules other than the main one.
832 if noPackage == nil || MainModules.Contains(noPackage.Mod.Path) {
835 case *NoMatchingVersionError:
836 if noVersion == nil {
839 case *NoPatchBaseError:
840 if noPatchBase == nil {
843 case *module.InvalidPathError:
844 // The prefix was not a valid module path, and there was no replacement.
845 // Prefixes like this may appear in candidateModules, since we handle
846 // replaced modules that weren't required in the repo lookup process
849 // A shorter prefix may be a valid module path and may contain a valid
850 // import path, so this is a low-priority error.
851 if invalidPath == nil {
855 if errors.Is(rErr, fs.ErrNotExist) {
856 if notExistErr == nil {
859 } else if err == nil {
860 if len(found) > 0 || noPackage != nil {
861 // golang.org/issue/34094: If we have already found a module that
862 // could potentially contain the target package, ignore unclassified
863 // errors for modules with shorter paths.
865 // golang.org/issue/34383 is a special case of this: if we have
866 // already found example.com/foo/v2@v2.0.0 with a matching go.mod
867 // file, ignore the error from example.com/foo@v2.0.0.
875 // TODO(#26232): If len(found) == 0 and some of the errors are 4xx HTTP
876 // codes, have the auth package recheck the failed paths.
877 // If we obtain new credentials for any of them, re-run the above loop.
879 if len(found) == 0 && err == nil {
881 case noPackage != nil:
883 case noVersion != nil:
885 case noPatchBase != nil:
887 case invalidPath != nil:
889 case notExistErr != nil:
892 panic("queryPrefixModules: no modules found, but no error detected")
899 // A NoMatchingVersionError indicates that Query found a module at the requested
900 // path, but not at any versions satisfying the query string and allow-function.
902 // NOTE: NoMatchingVersionError MUST NOT implement Is(fs.ErrNotExist).
904 // If the module came from a proxy, that proxy had to return a successful status
905 // code for the versions it knows about, and thus did not have the opportunity
906 // to return a non-400 status code to suppress fallback.
907 type NoMatchingVersionError struct {
908 query, current string
911 func (e *NoMatchingVersionError) Error() string {
913 if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" {
914 currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
916 return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
919 // A NoPatchBaseError indicates that Query was called with the query "patch"
920 // but with a current version of "" or "none".
921 type NoPatchBaseError struct {
925 func (e *NoPatchBaseError) Error() string {
926 return fmt.Sprintf(`can't query version "patch" of module %s: no existing version is required`, e.path)
929 // A WildcardInFirstElementError indicates that a pattern passed to QueryPattern
930 // had a wildcard in its first path element, and therefore had no pattern-prefix
931 // modules to search in.
932 type WildcardInFirstElementError struct {
937 func (e *WildcardInFirstElementError) Error() string {
938 return fmt.Sprintf("no modules to query for %s@%s because first path element contains a wildcard", e.Pattern, e.Query)
941 // A PackageNotInModuleError indicates that QueryPattern found a candidate
942 // module at the requested version, but that module did not contain any packages
943 // matching the requested pattern.
945 // NOTE: PackageNotInModuleError MUST NOT implement Is(fs.ErrNotExist).
947 // If the module came from a proxy, that proxy had to return a successful status
948 // code for the versions it knows about, and thus did not have the opportunity
949 // to return a non-400 status code to suppress fallback.
950 type PackageNotInModuleError struct {
951 MainModules []module.Version
953 Replacement module.Version
958 func (e *PackageNotInModuleError) Error() string {
959 if len(e.MainModules) > 0 {
960 prefix := "workspace modules do"
961 if len(e.MainModules) == 1 {
962 prefix = fmt.Sprintf("main module (%s) does", e.MainModules[0])
964 if strings.Contains(e.Pattern, "...") {
965 return fmt.Sprintf("%s not contain packages matching %s", prefix, e.Pattern)
967 return fmt.Sprintf("%s not contain package %s", prefix, e.Pattern)
971 if r := e.Replacement; r.Path != "" {
972 replacement := r.Path
974 replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
976 if e.Query == e.Mod.Version {
977 found = fmt.Sprintf(" (replaced by %s)", replacement)
979 found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
981 } else if e.Query != e.Mod.Version {
982 found = fmt.Sprintf(" (%s)", e.Mod.Version)
985 if strings.Contains(e.Pattern, "...") {
986 return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
988 return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
991 func (e *PackageNotInModuleError) ImportPath() string {
992 if !strings.Contains(e.Pattern, "...") {
998 // versionHasGoMod returns whether a version has a go.mod file.
1000 // versionHasGoMod fetches the go.mod file (possibly a fake) and true if it
1001 // contains anything other than a module directive with the same path. When a
1002 // module does not have a real go.mod file, the go command acts as if it had one
1003 // that only contained a module directive. Normal go.mod files created after
1004 // 1.12 at least have a go directive.
1006 // This function is a heuristic, since it's possible to commit a file that would
1007 // pass this test. However, we only need a heuristic for determining whether
1008 // +incompatible versions may be "latest", which is what this function is used
1011 // This heuristic is useful for two reasons: first, when using a proxy,
1012 // this lets us fetch from the .mod endpoint which is much faster than the .zip
1013 // endpoint. The .mod file is used anyway, even if the .zip file contains a
1014 // go.mod with different content. Second, if we don't fetch the .zip, then
1015 // we don't need to verify it in go.sum. This makes 'go list -m -u' faster
1017 func versionHasGoMod(_ context.Context, m module.Version) (bool, error) {
1018 _, data, err := rawGoModData(m)
1022 isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path))
1026 // A versionRepo is a subset of modfetch.Repo that can report information about
1027 // available versions, but cannot fetch specific source files.
1028 type versionRepo interface {
1030 CheckReuse(context.Context, *codehost.Origin) error
1031 Versions(ctx context.Context, prefix string) (*modfetch.Versions, error)
1032 Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error)
1033 Latest(context.Context) (*modfetch.RevInfo, error)
1036 var _ versionRepo = modfetch.Repo(nil)
1038 func lookupRepo(ctx context.Context, proxy, path string) (repo versionRepo, err error) {
1039 if path != "go" && path != "toolchain" {
1040 err = module.CheckPath(path)
1043 repo = modfetch.Lookup(ctx, proxy, path)
1045 repo = emptyRepo{path: path, err: err}
1048 if MainModules == nil {
1050 } else if _, ok := MainModules.HighestReplaced()[path]; ok {
1051 return &replacementRepo{repo: repo}, nil
1057 // An emptyRepo is a versionRepo that contains no versions.
1058 type emptyRepo struct {
1063 var _ versionRepo = emptyRepo{}
1065 func (er emptyRepo) ModulePath() string { return er.path }
1066 func (er emptyRepo) CheckReuse(ctx context.Context, old *codehost.Origin) error {
1067 return fmt.Errorf("empty repo")
1069 func (er emptyRepo) Versions(ctx context.Context, prefix string) (*modfetch.Versions, error) {
1070 return &modfetch.Versions{}, nil
1072 func (er emptyRepo) Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error) {
1075 func (er emptyRepo) Latest(ctx context.Context) (*modfetch.RevInfo, error) { return nil, er.err }
1077 // A replacementRepo augments a versionRepo to include the replacement versions
1078 // (if any) found in the main module's go.mod file.
1080 // A replacementRepo suppresses "not found" errors for otherwise-nonexistent
1081 // modules, so a replacementRepo should only be constructed for a module that
1082 // actually has one or more valid replacements.
1083 type replacementRepo struct {
1087 var _ versionRepo = (*replacementRepo)(nil)
1089 func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() }
1091 func (rr *replacementRepo) CheckReuse(ctx context.Context, old *codehost.Origin) error {
1092 return fmt.Errorf("replacement repo")
1095 // Versions returns the versions from rr.repo augmented with any matching
1096 // replacement versions.
1097 func (rr *replacementRepo) Versions(ctx context.Context, prefix string) (*modfetch.Versions, error) {
1098 repoVersions, err := rr.repo.Versions(ctx, prefix)
1100 if !errors.Is(err, os.ErrNotExist) {
1103 repoVersions = new(modfetch.Versions)
1106 versions := repoVersions.List
1107 for _, mm := range MainModules.Versions() {
1108 if index := MainModules.Index(mm); index != nil && len(index.replace) > 0 {
1109 path := rr.ModulePath()
1110 for m := range index.replace {
1111 if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) {
1112 versions = append(versions, m.Version)
1118 if len(versions) == len(repoVersions.List) { // replacement versions added
1119 return repoVersions, nil
1122 path := rr.ModulePath()
1123 sort.Slice(versions, func(i, j int) bool {
1124 return gover.ModCompare(path, versions[i], versions[j]) < 0
1127 return &modfetch.Versions{List: versions}, nil
1130 func (rr *replacementRepo) Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error) {
1131 info, err := rr.repo.Stat(ctx, rev)
1135 var hasReplacements bool
1136 for _, v := range MainModules.Versions() {
1137 if index := MainModules.Index(v); index != nil && len(index.replace) > 0 {
1138 hasReplacements = true
1141 if !hasReplacements {
1145 v := module.CanonicalVersion(rev)
1147 // The replacements in the go.mod file list only canonical semantic versions,
1148 // so a non-canonical version can't possibly have a replacement.
1152 path := rr.ModulePath()
1153 _, pathMajor, ok := module.SplitPathVersion(path)
1154 if ok && pathMajor == "" {
1155 if err := module.CheckPathMajor(v, pathMajor); err != nil && semver.Build(v) == "" {
1156 v += "+incompatible"
1160 if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
1163 return rr.replacementStat(v)
1166 func (rr *replacementRepo) Latest(ctx context.Context) (*modfetch.RevInfo, error) {
1167 info, err := rr.repo.Latest(ctx)
1168 path := rr.ModulePath()
1170 if v, ok := MainModules.HighestReplaced()[path]; ok {
1172 // The only replacement is a wildcard that doesn't specify a version, so
1173 // synthesize a pseudo-version with an appropriate major version and a
1174 // timestamp below any real timestamp. That way, if the main module is
1175 // used from within some other module, the user will be able to upgrade
1176 // the requirement to any real version they choose.
1177 if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 {
1178 v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
1180 v = module.PseudoVersion("v0", "", time.Time{}, "000000000000")
1184 if err != nil || gover.ModCompare(path, v, info.Version) > 0 {
1185 return rr.replacementStat(v)
1192 func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) {
1193 rev := &modfetch.RevInfo{Version: v}
1194 if module.IsPseudoVersion(v) {
1195 rev.Time, _ = module.PseudoVersionTime(v)
1196 rev.Short, _ = module.PseudoVersionRev(v)
1201 // A QueryMatchesMainModulesError indicates that a query requests
1202 // a version of the main module that cannot be satisfied.
1203 // (The main module's version cannot be changed.)
1204 type QueryMatchesMainModulesError struct {
1205 MainModules []module.Version
1210 func (e *QueryMatchesMainModulesError) Error() string {
1211 if MainModules.Contains(e.Pattern) {
1212 return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern)
1216 mainModulePaths := make([]string, len(e.MainModules))
1217 for i := range e.MainModules {
1218 mainModulePaths[i] = e.MainModules[i].Path
1220 if len(e.MainModules) > 1 {
1223 return fmt.Sprintf("can't request version %q of pattern %q that includes the main module%s (%s)", e.Query, e.Pattern, plural, strings.Join(mainModulePaths, ", "))
1226 // A QueryUpgradesAllError indicates that a query requests
1227 // an upgrade on the all pattern.
1228 // (The main module's version cannot be changed.)
1229 type QueryUpgradesAllError struct {
1230 MainModules []module.Version
1234 func (e *QueryUpgradesAllError) Error() string {
1235 var plural string = ""
1236 if len(e.MainModules) != 1 {
1240 return fmt.Sprintf("can't request version %q of pattern \"all\" that includes the main module%s", e.Query, plural)
1243 // A QueryMatchesPackagesInMainModuleError indicates that a query cannot be
1244 // satisfied because it matches one or more packages found in the main module.
1245 type QueryMatchesPackagesInMainModuleError struct {
1251 func (e *QueryMatchesPackagesInMainModuleError) Error() string {
1252 if len(e.Packages) > 1 {
1253 return fmt.Sprintf("pattern %s matches %d packages in the main module, so can't request version %s", e.Pattern, len(e.Packages), e.Query)
1256 if search.IsMetaPackage(e.Pattern) || strings.Contains(e.Pattern, "...") {
1257 return fmt.Sprintf("pattern %s matches package %s in the main module, so can't request version %s", e.Pattern, e.Packages[0], e.Query)
1260 return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query)