]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/modload/query.go
cmd/go: additional doc-inspired tests and bug fixes
[gostls13.git] / src / cmd / go / internal / modload / query.go
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.
4
5 package modload
6
7 import (
8         "bytes"
9         "context"
10         "errors"
11         "fmt"
12         "io/fs"
13         "os"
14         pathpkg "path"
15         "slices"
16         "sort"
17         "strings"
18         "sync"
19         "time"
20
21         "cmd/go/internal/cfg"
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"
28         "cmd/go/internal/str"
29         "cmd/go/internal/trace"
30         "cmd/internal/pkgpattern"
31
32         "golang.org/x/mod/module"
33         "golang.org/x/mod/semver"
34 )
35
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:
39 //
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
43 //     recent commit.
44 //
45 //   - the literal string "upgrade", equivalent to "latest" except that if
46 //     current is a newer version, current will be returned (see below).
47 //
48 //   - the literal string "patch", denoting the latest available tagged version
49 //     with the same major and minor number as current (see below).
50 //
51 //   - v1, denoting the latest available tagged version v1.x.x.
52 //
53 //   - v1.2, denoting the latest available tagged version v1.2.x.
54 //
55 //   - v1.2.3, a semantic version string denoting that tagged version.
56 //
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.
60 //
61 //   - a repository commit identifier or tag, denoting that commit.
62 //
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.
70 //
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.
77 //
78 // If path is the path of the main module and the query is "latest",
79 // Query returns Target.Version as the version.
80 //
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)
85         defer span.Done()
86
87         return queryReuse(ctx, path, query, current, allowed, nil)
88 }
89
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)
96                 return err
97         })
98         return info, err
99 }
100
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)
106                 if err != nil {
107                         return err
108                 }
109                 return repo.CheckReuse(ctx, old)
110         })
111 }
112
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.
116 //
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
123
124 var errQueryDisabled error = queryDisabledError{}
125
126 type queryDisabledError struct{}
127
128 func (queryDisabledError) Error() string {
129         if cfg.BuildModReason == "" {
130                 return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod)
131         }
132         return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
133 }
134
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)
137         defer span.Done()
138
139         if current != "" && current != "none" && !gover.ModIsValid(path, current) {
140                 return nil, fmt.Errorf("invalid previous version %v@%v", path, current)
141         }
142         if cfg.BuildMod == "vendor" {
143                 return nil, errQueryDisabled
144         }
145         if allowed == nil {
146                 allowed = func(context.Context, module.Version) error { return nil }
147         }
148
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)
153                 }
154                 return &modfetch.RevInfo{Version: m.Version}, nil
155         }
156
157         if path == "std" || path == "cmd" {
158                 return nil, fmt.Errorf("can't query specific version (%q) of standard-library module %q", query, path)
159         }
160
161         repo, err := lookupRepo(ctx, proxy, path)
162         if err != nil {
163                 return nil, err
164         }
165
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,
170                                 Origin:  old.Origin,
171                         }
172                         if old.Time != nil {
173                                 info.Time = *old.Time
174                         }
175                         return info, nil
176                 }
177         }
178
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
184                 // version.
185
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)
190                 if err != nil {
191                         queryErr := err
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) {
199                                         return info, err
200                                 }
201                         }
202                         if err != nil {
203                                 return info, queryErr
204                         }
205                 }
206                 if err := allowed(ctx, module.Version{Path: path, Version: info.Version}); errors.Is(err, ErrDisallowed) {
207                         return nil, err
208                 }
209                 return info, nil
210         } else if err != nil {
211                 return nil, err
212         }
213
214         // Load versions and execute query.
215         versions, err := repo.Versions(ctx, qm.prefix)
216         if err != nil {
217                 return nil, err
218         }
219         revErr := &modfetch.RevInfo{Origin: versions.Origin} // RevInfo to return with error
220
221         releases, prereleases, err := qm.filterVersions(ctx, versions.List)
222         if err != nil {
223                 return revErr, err
224         }
225
226         mergeRevOrigin := func(rev *modfetch.RevInfo, origin *codehost.Origin) *modfetch.RevInfo {
227                 merged := mergeOrigin(rev.Origin, origin)
228                 if merged == rev.Origin {
229                         return rev
230                 }
231                 clone := new(modfetch.RevInfo)
232                 *clone = *rev
233                 clone.Origin = merged
234                 return clone
235         }
236
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 {
242                         return revErr, err
243                 }
244                 rev = mergeRevOrigin(rev, versions.Origin)
245                 if err != nil {
246                         return rev, err
247                 }
248
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.
252                         //
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
257                         // in time.
258                         //
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) {
271                                         return revErr, err
272                                 }
273                                 rev, err = repo.Stat(ctx, current)
274                                 if rev == nil && err != nil {
275                                         return revErr, err
276                                 }
277                                 rev = mergeRevOrigin(rev, versions.Origin)
278                                 return rev, err
279                         }
280                 }
281
282                 return rev, nil
283         }
284
285         if qm.preferLower {
286                 if len(releases) > 0 {
287                         return lookup(releases[0])
288                 }
289                 if len(prereleases) > 0 {
290                         return lookup(prereleases[0])
291                 }
292         } else {
293                 if len(releases) > 0 {
294                         return lookup(releases[len(releases)-1])
295                 }
296                 if len(prereleases) > 0 {
297                         return lookup(prereleases[len(prereleases)-1])
298                 }
299         }
300
301         if qm.mayUseLatest {
302                 latest, err := repo.Latest(ctx)
303                 if err == nil {
304                         if qm.allowsVersion(ctx, latest.Version) {
305                                 return lookup(latest.Version)
306                         }
307                 } else if !errors.Is(err, fs.ErrNotExist) {
308                         return revErr, err
309                 }
310         }
311
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) {
315                         return nil, err
316                 }
317                 return lookup(current)
318         }
319
320         return revErr, &NoMatchingVersionError{query: query, current: current}
321 }
322
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" ||
329                 vers == "upgrade" ||
330                 vers == "patch" ||
331                 strings.HasPrefix(vers, "<") ||
332                 strings.HasPrefix(vers, ">") ||
333                 (gover.ModIsValid(path, vers) && gover.ModIsPrefix(path, vers)) {
334                 return false
335         }
336         return true
337 }
338
339 type queryMatcher struct {
340         path               string
341         prefix             string
342         filter             func(version string) bool
343         allowed            AllowedFunc
344         canStat            bool // if true, the query can be resolved by repo.Stat
345         preferLower        bool // if true, choose the lowest matching version
346         mayUseLatest       bool
347         preferIncompatible bool
348 }
349
350 var errRevQuery = errors.New("query refers to a non-semver revision")
351
352 // newQueryMatcher returns a new queryMatcher that matches the versions
353 // specified by the given query on the module with the given path.
354 //
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)
360         }
361
362         matchesMajor := func(v string) bool {
363                 _, pathMajor, ok := module.SplitPathVersion(path)
364                 if !ok {
365                         return false
366                 }
367                 return module.CheckPathMajor(v, pathMajor) == nil
368         }
369
370         qm := &queryMatcher{
371                 path:               path,
372                 allowed:            allowed,
373                 preferIncompatible: strings.HasSuffix(current, "+incompatible"),
374         }
375
376         switch {
377         case query == "latest":
378                 qm.mayUseLatest = true
379
380         case query == "upgrade":
381                 if current == "" || current == "none" {
382                         qm.mayUseLatest = true
383                 } else {
384                         qm.mayUseLatest = module.IsPseudoVersion(current)
385                         qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, current) >= 0 }
386                 }
387
388         case query == "patch":
389                 if current == "" || current == "none" {
390                         return nil, &NoPatchBaseError{path}
391                 }
392                 if current == "" {
393                         qm.mayUseLatest = true
394                 } else {
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 }
398                 }
399
400         case strings.HasPrefix(query, "<="):
401                 v := query[len("<="):]
402                 if !gover.ModIsValid(path, v) {
403                         return badVersion(v)
404                 }
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)
408                 }
409                 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) <= 0 }
410                 if !matchesMajor(v) {
411                         qm.preferIncompatible = true
412                 }
413
414         case strings.HasPrefix(query, "<"):
415                 v := query[len("<"):]
416                 if !gover.ModIsValid(path, v) {
417                         return badVersion(v)
418                 }
419                 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) < 0 }
420                 if !matchesMajor(v) {
421                         qm.preferIncompatible = true
422                 }
423
424         case strings.HasPrefix(query, ">="):
425                 v := query[len(">="):]
426                 if !gover.ModIsValid(path, v) {
427                         return badVersion(v)
428                 }
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
433                 }
434
435         case strings.HasPrefix(query, ">"):
436                 v := query[len(">"):]
437                 if !gover.ModIsValid(path, v) {
438                         return badVersion(v)
439                 }
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)
443                 }
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
448                 }
449
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 }
456                 } else {
457                         qm.canStat = true
458                         qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, query) == 0 }
459                         qm.prefix = semver.Canonical(query)
460                 }
461                 if !matchesMajor(query) {
462                         qm.preferIncompatible = true
463                 }
464
465         default:
466                 return nil, errRevQuery
467         }
468
469         return qm, nil
470 }
471
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) {
476                 return false
477         }
478         if qm.filter != nil && !qm.filter(v) {
479                 return false
480         }
481         if qm.allowed != nil {
482                 if err := qm.allowed(ctx, module.Version{Path: qm.path, Version: v}); errors.Is(err, ErrDisallowed) {
483                         return false
484                 }
485         }
486         return true
487 }
488
489 // filterVersions classifies versions into releases and pre-releases, filtering
490 // out:
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.
494 //
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
499
500         var lastCompatible string
501         for _, v := range versions {
502                 if !qm.allowsVersion(ctx, v) {
503                         continue
504                 }
505
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).
515                         //
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.
522
523                         if !strings.HasSuffix(v, "+incompatible") {
524                                 lastCompatible = v
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})
531                                 if err != nil {
532                                         return nil, nil, err
533                                 }
534                                 if ok {
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
538                                         // here onward.
539                                         break
540                                 }
541
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
546                         }
547                 }
548
549                 if gover.ModIsPrerelease(qm.path, v) {
550                         prereleases = append(prereleases, v)
551                 } else {
552                         releases = append(releases, v)
553                 }
554         }
555
556         return releases, prereleases, nil
557 }
558
559 type QueryResult struct {
560         Mod      module.Version
561         Rev      *modfetch.RevInfo
562         Packages []string
563 }
564
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)
569
570         if len(pkgMods) == 0 && err == nil {
571                 replacement := Replacement(modOnly.Mod)
572                 return nil, &PackageNotInModuleError{
573                         Mod:         modOnly.Mod,
574                         Replacement: replacement,
575                         Query:       query,
576                         Pattern:     pattern,
577                 }
578         }
579
580         return pkgMods, err
581 }
582
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.
587 //
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".
591 //
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
594 // possible modules.
595 //
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)
600         defer span.Done()
601
602         base := pattern
603
604         firstError := func(m *search.Match) error {
605                 if len(m.Errs) == 0 {
606                         return nil
607                 }
608                 return m.Errs[0]
609         }
610
611         var match func(mod module.Version, roots []string, isLocal bool) *search.Match
612         matchPattern := pkgpattern.MatchPattern(pattern)
613
614         if i := strings.Index(pattern, "..."); i >= 0 {
615                 base = pathpkg.Dir(pattern[:i+3])
616                 if base == "." {
617                         return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query}
618                 }
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})
622                         return m
623                 }
624         } else {
625                 match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
626                         m := search.NewMatch(pattern)
627                         prefix := mod.Path
628                         if MainModules.Contains(mod.Path) {
629                                 prefix = MainModules.PathPrefix(module.Version{Path: mod.Path})
630                         }
631                         for _, root := range roots {
632                                 if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
633                                         m.AddError(err)
634                                 } else if ok {
635                                         m.Pkgs = []string{pattern}
636                                 }
637                         }
638                         return m
639                 }
640         }
641
642         var mainModuleMatches []module.Version
643         for _, mainModule := range MainModules.Versions() {
644                 m := match(mainModule, modRoots, true)
645                 if len(m.Pkgs) > 0 {
646                         if query != "upgrade" && query != "patch" {
647                                 return nil, nil, &QueryMatchesPackagesInMainModuleError{
648                                         Pattern:  pattern,
649                                         Query:    query,
650                                         Packages: m.Pkgs,
651                                 }
652                         }
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)
655                         }
656                         return []QueryResult{{
657                                 Mod:      mainModule,
658                                 Rev:      &modfetch.RevInfo{Version: mainModule.Version},
659                                 Packages: m.Pkgs,
660                         }}, nil, nil
661                 }
662                 if err := firstError(m); err != nil {
663                         return nil, nil, err
664                 }
665
666                 var matchesMainModule bool
667                 if matchPattern(mainModule.Path) {
668                         mainModuleMatches = append(mainModuleMatches, mainModule)
669                         matchesMainModule = true
670                 }
671
672                 if (query == "upgrade" || query == "patch") && matchesMainModule {
673                         if err := allowed(ctx, mainModule); err == nil {
674                                 modOnly = &QueryResult{
675                                         Mod: mainModule,
676                                         Rev: &modfetch.RevInfo{Version: mainModule.Version},
677                                 }
678                         }
679                 }
680         }
681
682         var (
683                 results          []QueryResult
684                 candidateModules = modulePrefixesExcludingTarget(base)
685         )
686         if len(candidateModules) == 0 {
687                 if modOnly != nil {
688                         return nil, modOnly, nil
689                 } else if len(mainModuleMatches) != 0 {
690                         return nil, nil, &QueryMatchesMainModulesError{
691                                 MainModules: mainModuleMatches,
692                                 Pattern:     pattern,
693                                 Query:       query,
694                         }
695                 } else {
696                         return nil, nil, &PackageNotInModuleError{
697                                 MainModules: mainModuleMatches,
698                                 Query:       query,
699                                 Pattern:     pattern,
700                         }
701                 }
702         }
703
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)
707                         defer span.Done()
708
709                         pathCurrent := current(path)
710                         r.Mod.Path = path
711                         r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed, nil)
712                         if err != nil {
713                                 return r, err
714                         }
715                         r.Mod.Version = r.Rev.Version
716                         if gover.IsToolchain(r.Mod.Path) {
717                                 return r, nil
718                         }
719                         root, isLocal, err := fetch(ctx, r.Mod)
720                         if err != nil {
721                                 return r, err
722                         }
723                         m := match(r.Mod, []string{root}, isLocal)
724                         r.Packages = m.Pkgs
725                         if len(r.Packages) == 0 && !matchPattern(path) {
726                                 if err := firstError(m); err != nil {
727                                         return r, err
728                                 }
729                                 replacement := Replacement(r.Mod)
730                                 return r, &PackageNotInModuleError{
731                                         Mod:         r.Mod,
732                                         Replacement: replacement,
733                                         Query:       query,
734                                         Pattern:     pattern,
735                                 }
736                         }
737                         return r, nil
738                 }
739
740                 allResults, err := queryPrefixModules(ctx, candidateModules, queryModule)
741                 results = allResults[:0]
742                 for _, r := range allResults {
743                         if len(r.Packages) == 0 {
744                                 modOnly = &r
745                         } else {
746                                 results = append(results, r)
747                         }
748                 }
749                 return err
750         })
751
752         if len(mainModuleMatches) > 0 && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
753                 return nil, nil, &QueryMatchesMainModulesError{
754                         Pattern: pattern,
755                         Query:   query,
756                 }
757         }
758         return slices.Clip(results), modOnly, err
759 }
760
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)
768
769         mainModulePrefixes := make(map[string]bool)
770         for _, m := range MainModules.Versions() {
771                 mainModulePrefixes[m.Path] = true
772         }
773
774         for {
775                 if !mainModulePrefixes[path] {
776                         if _, _, ok := module.SplitPathVersion(path); ok {
777                                 prefixes = append(prefixes, path)
778                         }
779                 }
780
781                 j := strings.LastIndexByte(path, '/')
782                 if j < 0 {
783                         break
784                 }
785                 path = path[:j]
786         }
787
788         return prefixes
789 }
790
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")
793         defer span.Done()
794
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.
799         type result struct {
800                 QueryResult
801                 err error
802         }
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)
810                         wg.Done()
811                 }(p, &results[i])
812         }
813         wg.Wait()
814
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.
818         var (
819                 noPackage   *PackageNotInModuleError
820                 noVersion   *NoMatchingVersionError
821                 noPatchBase *NoPatchBaseError
822                 invalidPath *module.InvalidPathError // see comment in case below
823                 notExistErr error
824         )
825         for _, r := range results {
826                 switch rErr := r.err.(type) {
827                 case nil:
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) {
833                                 noPackage = rErr
834                         }
835                 case *NoMatchingVersionError:
836                         if noVersion == nil {
837                                 noVersion = rErr
838                         }
839                 case *NoPatchBaseError:
840                         if noPatchBase == nil {
841                                 noPatchBase = rErr
842                         }
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
847                         // (see lookupRepo).
848                         //
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 {
852                                 invalidPath = rErr
853                         }
854                 default:
855                         if errors.Is(rErr, fs.ErrNotExist) {
856                                 if notExistErr == nil {
857                                         notExistErr = rErr
858                                 }
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.
864
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.
868                                 } else {
869                                         err = r.err
870                                 }
871                         }
872                 }
873         }
874
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.
878
879         if len(found) == 0 && err == nil {
880                 switch {
881                 case noPackage != nil:
882                         err = noPackage
883                 case noVersion != nil:
884                         err = noVersion
885                 case noPatchBase != nil:
886                         err = noPatchBase
887                 case invalidPath != nil:
888                         err = invalidPath
889                 case notExistErr != nil:
890                         err = notExistErr
891                 default:
892                         panic("queryPrefixModules: no modules found, but no error detected")
893                 }
894         }
895
896         return found, err
897 }
898
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.
901 //
902 // NOTE: NoMatchingVersionError MUST NOT implement Is(fs.ErrNotExist).
903 //
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
909 }
910
911 func (e *NoMatchingVersionError) Error() string {
912         currentSuffix := ""
913         if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" {
914                 currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
915         }
916         return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
917 }
918
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 {
922         path string
923 }
924
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)
927 }
928
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 {
933         Pattern string
934         Query   string
935 }
936
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)
939 }
940
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.
944 //
945 // NOTE: PackageNotInModuleError MUST NOT implement Is(fs.ErrNotExist).
946 //
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
952         Mod         module.Version
953         Replacement module.Version
954         Query       string
955         Pattern     string
956 }
957
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])
963                 }
964                 if strings.Contains(e.Pattern, "...") {
965                         return fmt.Sprintf("%s not contain packages matching %s", prefix, e.Pattern)
966                 }
967                 return fmt.Sprintf("%s not contain package %s", prefix, e.Pattern)
968         }
969
970         found := ""
971         if r := e.Replacement; r.Path != "" {
972                 replacement := r.Path
973                 if r.Version != "" {
974                         replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
975                 }
976                 if e.Query == e.Mod.Version {
977                         found = fmt.Sprintf(" (replaced by %s)", replacement)
978                 } else {
979                         found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
980                 }
981         } else if e.Query != e.Mod.Version {
982                 found = fmt.Sprintf(" (%s)", e.Mod.Version)
983         }
984
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)
987         }
988         return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
989 }
990
991 func (e *PackageNotInModuleError) ImportPath() string {
992         if !strings.Contains(e.Pattern, "...") {
993                 return e.Pattern
994         }
995         return ""
996 }
997
998 // versionHasGoMod returns whether a version has a go.mod file.
999 //
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.
1005 //
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
1009 // for.
1010 //
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
1016 // and simpler.
1017 func versionHasGoMod(_ context.Context, m module.Version) (bool, error) {
1018         _, data, err := rawGoModData(m)
1019         if err != nil {
1020                 return false, err
1021         }
1022         isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path))
1023         return !isFake, nil
1024 }
1025
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 {
1029         ModulePath() string
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)
1034 }
1035
1036 var _ versionRepo = modfetch.Repo(nil)
1037
1038 func lookupRepo(ctx context.Context, proxy, path string) (repo versionRepo, err error) {
1039         if path != "go" && path != "toolchain" {
1040                 err = module.CheckPath(path)
1041         }
1042         if err == nil {
1043                 repo = modfetch.Lookup(ctx, proxy, path)
1044         } else {
1045                 repo = emptyRepo{path: path, err: err}
1046         }
1047
1048         if MainModules == nil {
1049                 return repo, err
1050         } else if _, ok := MainModules.HighestReplaced()[path]; ok {
1051                 return &replacementRepo{repo: repo}, nil
1052         }
1053
1054         return repo, err
1055 }
1056
1057 // An emptyRepo is a versionRepo that contains no versions.
1058 type emptyRepo struct {
1059         path string
1060         err  error
1061 }
1062
1063 var _ versionRepo = emptyRepo{}
1064
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")
1068 }
1069 func (er emptyRepo) Versions(ctx context.Context, prefix string) (*modfetch.Versions, error) {
1070         return &modfetch.Versions{}, nil
1071 }
1072 func (er emptyRepo) Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error) {
1073         return nil, er.err
1074 }
1075 func (er emptyRepo) Latest(ctx context.Context) (*modfetch.RevInfo, error) { return nil, er.err }
1076
1077 // A replacementRepo augments a versionRepo to include the replacement versions
1078 // (if any) found in the main module's go.mod file.
1079 //
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 {
1084         repo versionRepo
1085 }
1086
1087 var _ versionRepo = (*replacementRepo)(nil)
1088
1089 func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() }
1090
1091 func (rr *replacementRepo) CheckReuse(ctx context.Context, old *codehost.Origin) error {
1092         return fmt.Errorf("replacement repo")
1093 }
1094
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)
1099         if err != nil {
1100                 if !errors.Is(err, os.ErrNotExist) {
1101                         return nil, err
1102                 }
1103                 repoVersions = new(modfetch.Versions)
1104         }
1105
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)
1113                                 }
1114                         }
1115                 }
1116         }
1117
1118         if len(versions) == len(repoVersions.List) { // replacement versions added
1119                 return repoVersions, nil
1120         }
1121
1122         path := rr.ModulePath()
1123         sort.Slice(versions, func(i, j int) bool {
1124                 return gover.ModCompare(path, versions[i], versions[j]) < 0
1125         })
1126         str.Uniq(&versions)
1127         return &modfetch.Versions{List: versions}, nil
1128 }
1129
1130 func (rr *replacementRepo) Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error) {
1131         info, err := rr.repo.Stat(ctx, rev)
1132         if err == nil {
1133                 return info, err
1134         }
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
1139                 }
1140         }
1141         if !hasReplacements {
1142                 return info, err
1143         }
1144
1145         v := module.CanonicalVersion(rev)
1146         if v != 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.
1149                 return info, err
1150         }
1151
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"
1157                 }
1158         }
1159
1160         if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
1161                 return info, err
1162         }
1163         return rr.replacementStat(v)
1164 }
1165
1166 func (rr *replacementRepo) Latest(ctx context.Context) (*modfetch.RevInfo, error) {
1167         info, err := rr.repo.Latest(ctx)
1168         path := rr.ModulePath()
1169
1170         if v, ok := MainModules.HighestReplaced()[path]; ok {
1171                 if v == "" {
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")
1179                         } else {
1180                                 v = module.PseudoVersion("v0", "", time.Time{}, "000000000000")
1181                         }
1182                 }
1183
1184                 if err != nil || gover.ModCompare(path, v, info.Version) > 0 {
1185                         return rr.replacementStat(v)
1186                 }
1187         }
1188
1189         return info, err
1190 }
1191
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)
1197         }
1198         return rev, nil
1199 }
1200
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
1206         Pattern     string
1207         Query       string
1208 }
1209
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)
1213         }
1214
1215         plural := ""
1216         mainModulePaths := make([]string, len(e.MainModules))
1217         for i := range e.MainModules {
1218                 mainModulePaths[i] = e.MainModules[i].Path
1219         }
1220         if len(e.MainModules) > 1 {
1221                 plural = "s"
1222         }
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, ", "))
1224 }
1225
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
1231         Query       string
1232 }
1233
1234 func (e *QueryUpgradesAllError) Error() string {
1235         var plural string = ""
1236         if len(e.MainModules) != 1 {
1237                 plural = "s"
1238         }
1239
1240         return fmt.Sprintf("can't request version %q of pattern \"all\" that includes the main module%s", e.Query, plural)
1241 }
1242
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 {
1246         Pattern  string
1247         Query    string
1248         Packages []string
1249 }
1250
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)
1254         }
1255
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)
1258         }
1259
1260         return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query)
1261 }