...

Source file src/cmd/go/internal/modload/query.go

Documentation: cmd/go/internal/modload

     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
   102  // for a given module may be reused, according to the information in origin.
   103  func checkReuse(ctx context.Context, m module.Version, old *codehost.Origin) error {
   104  	return modfetch.TryProxies(func(proxy string) error {
   105  		repo, err := lookupRepo(ctx, proxy, m.Path)
   106  		if err != nil {
   107  			return err
   108  		}
   109  		return checkReuseRepo(ctx, repo, m.Path, m.Version, old)
   110  	})
   111  }
   112  
   113  func checkReuseRepo(ctx context.Context, repo versionRepo, path, query string, origin *codehost.Origin) error {
   114  	if origin == nil {
   115  		return errors.New("nil Origin")
   116  	}
   117  
   118  	// Ensure that the Origin actually includes enough fields to resolve the query.
   119  	// If we got the previous Origin data from a proxy, it may be missing something
   120  	// that we would have needed to resolve the query directly from the repo.
   121  	switch {
   122  	case origin.RepoSum != "":
   123  		// A RepoSum is always acceptable, since it incorporates everything
   124  		// (and is often associated with an error result).
   125  
   126  	case query == module.CanonicalVersion(query):
   127  		// This query refers to a specific version, and Go module versions
   128  		// are supposed to be cacheable and immutable (confirmed with checksums).
   129  		// If the version exists at all, we shouldn't need any extra information
   130  		// to identify which commit it resolves to.
   131  		//
   132  		// It may be associated with a Ref for a semantic-version tag, but if so
   133  		// we don't expect that tag to change in the future. We also don't need a
   134  		// TagSum: if a tag is removed from some ancestor commit, the version may
   135  		// change from valid to invalid, but we're ok with keeping stale versions
   136  		// as long as they were valid at some point in the past.
   137  		//
   138  		// If the version did not successfully resolve, the origin may indicate
   139  		// a TagSum and/or RepoSum instead of a Hash, in which case we still need
   140  		// to check those to ensure that the error is still applicable.
   141  		if origin.Hash == "" && origin.Ref == "" && origin.TagSum == "" {
   142  			return errors.New("no Origin information to check")
   143  		}
   144  
   145  	case IsRevisionQuery(path, query):
   146  		// This query may refer to a branch, non-version tag, or commit ID.
   147  		//
   148  		// If it is a commit ID, we expect to see a Hash in the Origin data. On
   149  		// the other hand, if it is not a commit ID, we expect to see either a Ref
   150  		// (for a positive result) or a RepoSum (for a negative result), since
   151  		// we don't expect refs in general to remain stable over time.
   152  		if origin.Hash == "" && origin.Ref == "" {
   153  			return fmt.Errorf("query %q requires a Hash or Ref", query)
   154  		}
   155  		// Once we resolve the query to a particular commit, we will need to
   156  		// also identify the most appropriate version to assign to that commit.
   157  		// (It may correspond to more than one valid version.)
   158  		//
   159  		// The most appropriate version depends on the tags associated with
   160  		// both the commit itself (if the commit is a tagged version)
   161  		// and its ancestors (if we need to produce a pseudo-version for it).
   162  		if origin.TagSum == "" {
   163  			return fmt.Errorf("query %q requires a TagSum", query)
   164  		}
   165  
   166  	default:
   167  		// The query may be "latest" or a version inequality or prefix.
   168  		// Its result depends on the absence of higher tags matching the query,
   169  		// not just the state of an individual ref or tag.
   170  		if origin.TagSum == "" {
   171  			return fmt.Errorf("query %q requires a TagSum", query)
   172  		}
   173  	}
   174  
   175  	return repo.CheckReuse(ctx, origin)
   176  }
   177  
   178  // AllowedFunc is used by Query and other functions to filter out unsuitable
   179  // versions, for example, those listed in exclude directives in the main
   180  // module's go.mod file.
   181  //
   182  // An AllowedFunc returns an error equivalent to ErrDisallowed for an unsuitable
   183  // version. Any other error indicates the function was unable to determine
   184  // whether the version should be allowed, for example, the function was unable
   185  // to fetch or parse a go.mod file containing retractions. Typically, errors
   186  // other than ErrDisallowed may be ignored.
   187  type AllowedFunc func(context.Context, module.Version) error
   188  
   189  var errQueryDisabled error = queryDisabledError{}
   190  
   191  type queryDisabledError struct{}
   192  
   193  func (queryDisabledError) Error() string {
   194  	if cfg.BuildModReason == "" {
   195  		return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod)
   196  	}
   197  	return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
   198  }
   199  
   200  func queryProxy(ctx context.Context, proxy, path, query, current string, allowed AllowedFunc, reuse map[module.Version]*modinfo.ModulePublic) (*modfetch.RevInfo, error) {
   201  	ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query)
   202  	defer span.Done()
   203  
   204  	if current != "" && current != "none" && !gover.ModIsValid(path, current) {
   205  		return nil, fmt.Errorf("invalid previous version %v@%v", path, current)
   206  	}
   207  	if cfg.BuildMod == "vendor" {
   208  		return nil, errQueryDisabled
   209  	}
   210  	if allowed == nil {
   211  		allowed = func(context.Context, module.Version) error { return nil }
   212  	}
   213  
   214  	if MainModules.Contains(path) && (query == "upgrade" || query == "patch") {
   215  		m := module.Version{Path: path}
   216  		if err := allowed(ctx, m); err != nil {
   217  			return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err)
   218  		}
   219  		return &modfetch.RevInfo{Version: m.Version}, nil
   220  	}
   221  
   222  	if path == "std" || path == "cmd" {
   223  		return nil, fmt.Errorf("can't query specific version (%q) of standard-library module %q", query, path)
   224  	}
   225  
   226  	repo, err := lookupRepo(ctx, proxy, path)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	if old := reuse[module.Version{Path: path, Version: query}]; old != nil {
   232  		if err := checkReuseRepo(ctx, repo, path, query, old.Origin); err == nil {
   233  			info := &modfetch.RevInfo{
   234  				Version: old.Version,
   235  				Origin:  old.Origin,
   236  			}
   237  			if old.Time != nil {
   238  				info.Time = *old.Time
   239  			}
   240  			return info, nil
   241  		}
   242  	}
   243  
   244  	// Parse query to detect parse errors (and possibly handle query)
   245  	// before any network I/O.
   246  	qm, err := newQueryMatcher(path, query, current, allowed)
   247  	if (err == nil && qm.canStat) || err == errRevQuery {
   248  		// Direct lookup of a commit identifier or complete (non-prefix) semantic
   249  		// version.
   250  
   251  		// If the identifier is not a canonical semver tag — including if it's a
   252  		// semver tag with a +metadata suffix — then modfetch.Stat will populate
   253  		// info.Version with a suitable pseudo-version.
   254  		info, err := repo.Stat(ctx, query)
   255  		if err != nil {
   256  			queryErr := err
   257  			// The full query doesn't correspond to a tag. If it is a semantic version
   258  			// with a +metadata suffix, see if there is a tag without that suffix:
   259  			// semantic versioning defines them to be equivalent.
   260  			canonicalQuery := module.CanonicalVersion(query)
   261  			if canonicalQuery != "" && query != canonicalQuery {
   262  				info, err = repo.Stat(ctx, canonicalQuery)
   263  				if err != nil && !errors.Is(err, fs.ErrNotExist) {
   264  					return info, err
   265  				}
   266  			}
   267  			if err != nil {
   268  				return info, queryErr
   269  			}
   270  		}
   271  		if err := allowed(ctx, module.Version{Path: path, Version: info.Version}); errors.Is(err, ErrDisallowed) {
   272  			return nil, err
   273  		}
   274  		return info, nil
   275  	} else if err != nil {
   276  		return nil, err
   277  	}
   278  
   279  	// Load versions and execute query.
   280  	versions, err := repo.Versions(ctx, qm.prefix)
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  	origin := versions.Origin
   285  
   286  	revWithOrigin := func(rev *modfetch.RevInfo) *modfetch.RevInfo {
   287  		if rev == nil {
   288  			if origin == nil {
   289  				return nil
   290  			}
   291  			return &modfetch.RevInfo{Origin: origin}
   292  		}
   293  
   294  		clone := *rev
   295  		clone.Origin = origin
   296  		return &clone
   297  	}
   298  
   299  	releases, prereleases, err := qm.filterVersions(ctx, versions.List)
   300  	if err != nil {
   301  		return revWithOrigin(nil), err
   302  	}
   303  
   304  	lookup := func(v string) (*modfetch.RevInfo, error) {
   305  		rev, err := repo.Stat(ctx, v)
   306  		if rev != nil {
   307  			// Note that Stat can return a non-nil rev and a non-nil err,
   308  			// in order to provide origin information to make the error cacheable.
   309  			origin = mergeOrigin(origin, rev.Origin)
   310  		}
   311  		if err != nil {
   312  			return revWithOrigin(nil), err
   313  		}
   314  
   315  		if (query == "upgrade" || query == "patch") && module.IsPseudoVersion(current) && !rev.Time.IsZero() {
   316  			// Don't allow "upgrade" or "patch" to move from a pseudo-version
   317  			// to a chronologically older version or pseudo-version.
   318  			//
   319  			// If the current version is a pseudo-version from an untagged branch, it
   320  			// may be semantically lower than the "latest" release or the latest
   321  			// pseudo-version on the main branch. A user on such a version is unlikely
   322  			// to intend to “upgrade” to a version that already existed at that point
   323  			// in time.
   324  			//
   325  			// We do this only if the current version is a pseudo-version: if the
   326  			// version is tagged, the author of the dependency module has given us
   327  			// explicit information about their intended precedence of this version
   328  			// relative to other versions, and we shouldn't contradict that
   329  			// information. (For example, v1.0.1 might be a backport of a fix already
   330  			// incorporated into v1.1.0, in which case v1.0.1 would be chronologically
   331  			// newer but v1.1.0 is still an “upgrade”; or v1.0.2 might be a revert of
   332  			// an unsuccessful fix in v1.0.1, in which case the v1.0.2 commit may be
   333  			// older than the v1.0.1 commit despite the tag itself being newer.)
   334  			currentTime, err := module.PseudoVersionTime(current)
   335  			if err == nil && rev.Time.Before(currentTime) {
   336  				if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
   337  					return revWithOrigin(nil), err
   338  				}
   339  				rev, err = repo.Stat(ctx, current)
   340  				if rev != nil {
   341  					origin = mergeOrigin(origin, rev.Origin)
   342  				}
   343  				if err != nil {
   344  					return revWithOrigin(nil), err
   345  				}
   346  				return revWithOrigin(rev), nil
   347  			}
   348  		}
   349  
   350  		return revWithOrigin(rev), nil
   351  	}
   352  
   353  	if qm.preferLower {
   354  		if len(releases) > 0 {
   355  			return lookup(releases[0])
   356  		}
   357  		if len(prereleases) > 0 {
   358  			return lookup(prereleases[0])
   359  		}
   360  	} else {
   361  		if len(releases) > 0 {
   362  			return lookup(releases[len(releases)-1])
   363  		}
   364  		if len(prereleases) > 0 {
   365  			return lookup(prereleases[len(prereleases)-1])
   366  		}
   367  	}
   368  
   369  	if qm.mayUseLatest {
   370  		latest, err := repo.Latest(ctx)
   371  		if latest != nil {
   372  			origin = mergeOrigin(origin, latest.Origin)
   373  		}
   374  		if err == nil {
   375  			if qm.allowsVersion(ctx, latest.Version) {
   376  				return lookup(latest.Version)
   377  			}
   378  		} else if !errors.Is(err, fs.ErrNotExist) {
   379  			return revWithOrigin(nil), err
   380  		}
   381  	}
   382  
   383  	if (query == "upgrade" || query == "patch") && current != "" && current != "none" {
   384  		// "upgrade" and "patch" may stay on the current version if allowed.
   385  		if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
   386  			return revWithOrigin(nil), err
   387  		}
   388  		return lookup(current)
   389  	}
   390  
   391  	return revWithOrigin(nil), &NoMatchingVersionError{query: query, current: current}
   392  }
   393  
   394  // IsRevisionQuery returns true if vers is a version query that may refer to
   395  // a particular version or revision in a repository like "v1.0.0", "master",
   396  // or "0123abcd". IsRevisionQuery returns false if vers is a query that
   397  // chooses from among available versions like "latest" or ">v1.0.0".
   398  func IsRevisionQuery(path, vers string) bool {
   399  	if vers == "latest" ||
   400  		vers == "upgrade" ||
   401  		vers == "patch" ||
   402  		strings.HasPrefix(vers, "<") ||
   403  		strings.HasPrefix(vers, ">") ||
   404  		(gover.ModIsValid(path, vers) && gover.ModIsPrefix(path, vers)) {
   405  		return false
   406  	}
   407  	return true
   408  }
   409  
   410  type queryMatcher struct {
   411  	path               string
   412  	prefix             string
   413  	filter             func(version string) bool
   414  	allowed            AllowedFunc
   415  	canStat            bool // if true, the query can be resolved by repo.Stat
   416  	preferLower        bool // if true, choose the lowest matching version
   417  	mayUseLatest       bool
   418  	preferIncompatible bool
   419  }
   420  
   421  var errRevQuery = errors.New("query refers to a non-semver revision")
   422  
   423  // newQueryMatcher returns a new queryMatcher that matches the versions
   424  // specified by the given query on the module with the given path.
   425  //
   426  // If the query can only be resolved by statting a non-SemVer revision,
   427  // newQueryMatcher returns errRevQuery.
   428  func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (*queryMatcher, error) {
   429  	badVersion := func(v string) (*queryMatcher, error) {
   430  		return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query)
   431  	}
   432  
   433  	matchesMajor := func(v string) bool {
   434  		_, pathMajor, ok := module.SplitPathVersion(path)
   435  		if !ok {
   436  			return false
   437  		}
   438  		return module.CheckPathMajor(v, pathMajor) == nil
   439  	}
   440  
   441  	qm := &queryMatcher{
   442  		path:               path,
   443  		allowed:            allowed,
   444  		preferIncompatible: strings.HasSuffix(current, "+incompatible"),
   445  	}
   446  
   447  	switch {
   448  	case query == "latest":
   449  		qm.mayUseLatest = true
   450  
   451  	case query == "upgrade":
   452  		if current == "" || current == "none" {
   453  			qm.mayUseLatest = true
   454  		} else {
   455  			qm.mayUseLatest = module.IsPseudoVersion(current)
   456  			qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, current) >= 0 }
   457  		}
   458  
   459  	case query == "patch":
   460  		if current == "" || current == "none" {
   461  			return nil, &NoPatchBaseError{path}
   462  		}
   463  		if current == "" {
   464  			qm.mayUseLatest = true
   465  		} else {
   466  			qm.mayUseLatest = module.IsPseudoVersion(current)
   467  			qm.prefix = gover.ModMajorMinor(qm.path, current) + "."
   468  			qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, current) >= 0 }
   469  		}
   470  
   471  	case strings.HasPrefix(query, "<="):
   472  		v := query[len("<="):]
   473  		if !gover.ModIsValid(path, v) {
   474  			return badVersion(v)
   475  		}
   476  		if gover.ModIsPrefix(path, v) {
   477  			// Refuse to say whether <=v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3).
   478  			return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
   479  		}
   480  		qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) <= 0 }
   481  		if !matchesMajor(v) {
   482  			qm.preferIncompatible = true
   483  		}
   484  
   485  	case strings.HasPrefix(query, "<"):
   486  		v := query[len("<"):]
   487  		if !gover.ModIsValid(path, v) {
   488  			return badVersion(v)
   489  		}
   490  		qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) < 0 }
   491  		if !matchesMajor(v) {
   492  			qm.preferIncompatible = true
   493  		}
   494  
   495  	case strings.HasPrefix(query, ">="):
   496  		v := query[len(">="):]
   497  		if !gover.ModIsValid(path, v) {
   498  			return badVersion(v)
   499  		}
   500  		qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) >= 0 }
   501  		qm.preferLower = true
   502  		if !matchesMajor(v) {
   503  			qm.preferIncompatible = true
   504  		}
   505  
   506  	case strings.HasPrefix(query, ">"):
   507  		v := query[len(">"):]
   508  		if !gover.ModIsValid(path, v) {
   509  			return badVersion(v)
   510  		}
   511  		if gover.ModIsPrefix(path, v) {
   512  			// Refuse to say whether >v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3).
   513  			return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
   514  		}
   515  		qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) > 0 }
   516  		qm.preferLower = true
   517  		if !matchesMajor(v) {
   518  			qm.preferIncompatible = true
   519  		}
   520  
   521  	case gover.ModIsValid(path, query):
   522  		if gover.ModIsPrefix(path, query) {
   523  			qm.prefix = query + "."
   524  			// Do not allow the query "v1.2" to match versions lower than "v1.2.0",
   525  			// such as prereleases for that version. (https://golang.org/issue/31972)
   526  			qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, query) >= 0 }
   527  		} else {
   528  			qm.canStat = true
   529  			qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, query) == 0 }
   530  			qm.prefix = semver.Canonical(query)
   531  		}
   532  		if !matchesMajor(query) {
   533  			qm.preferIncompatible = true
   534  		}
   535  
   536  	default:
   537  		return nil, errRevQuery
   538  	}
   539  
   540  	return qm, nil
   541  }
   542  
   543  // allowsVersion reports whether version v is allowed by the prefix, filter, and
   544  // AllowedFunc of qm.
   545  func (qm *queryMatcher) allowsVersion(ctx context.Context, v string) bool {
   546  	if qm.prefix != "" && !strings.HasPrefix(v, qm.prefix) {
   547  		if gover.IsToolchain(qm.path) && strings.TrimSuffix(qm.prefix, ".") == v {
   548  			// Allow 1.21 to match "1.21." prefix.
   549  		} else {
   550  			return false
   551  		}
   552  	}
   553  	if qm.filter != nil && !qm.filter(v) {
   554  		return false
   555  	}
   556  	if qm.allowed != nil {
   557  		if err := qm.allowed(ctx, module.Version{Path: qm.path, Version: v}); errors.Is(err, ErrDisallowed) {
   558  			return false
   559  		}
   560  	}
   561  	return true
   562  }
   563  
   564  // filterVersions classifies versions into releases and pre-releases, filtering
   565  // out:
   566  //  1. versions that do not satisfy the 'allowed' predicate, and
   567  //  2. "+incompatible" versions, if a compatible one satisfies the predicate
   568  //     and the incompatible version is not preferred.
   569  //
   570  // If the allowed predicate returns an error not equivalent to ErrDisallowed,
   571  // filterVersions returns that error.
   572  func (qm *queryMatcher) filterVersions(ctx context.Context, versions []string) (releases, prereleases []string, err error) {
   573  	needIncompatible := qm.preferIncompatible
   574  
   575  	var lastCompatible string
   576  	for _, v := range versions {
   577  		if !qm.allowsVersion(ctx, v) {
   578  			continue
   579  		}
   580  
   581  		if !needIncompatible {
   582  			// We're not yet sure whether we need to include +incompatible versions.
   583  			// Keep track of the last compatible version we've seen, and use the
   584  			// presence (or absence) of a go.mod file in that version to decide: a
   585  			// go.mod file implies that the module author is supporting modules at a
   586  			// compatible version (and we should ignore +incompatible versions unless
   587  			// requested explicitly), while a lack of go.mod file implies the
   588  			// potential for legacy (pre-modules) versioning without semantic import
   589  			// paths (and thus *with* +incompatible versions).
   590  			//
   591  			// This isn't strictly accurate if the latest compatible version has been
   592  			// replaced by a local file path, because we do not allow file-path
   593  			// replacements without a go.mod file: the user would have needed to add
   594  			// one. However, replacing the last compatible version while
   595  			// simultaneously expecting to upgrade implicitly to a +incompatible
   596  			// version seems like an extreme enough corner case to ignore for now.
   597  
   598  			if !strings.HasSuffix(v, "+incompatible") {
   599  				lastCompatible = v
   600  			} else if lastCompatible != "" {
   601  				// If the latest compatible version is allowed and has a go.mod file,
   602  				// ignore any version with a higher (+incompatible) major version. (See
   603  				// https://golang.org/issue/34165.) Note that we even prefer a
   604  				// compatible pre-release over an incompatible release.
   605  				ok, err := versionHasGoMod(ctx, module.Version{Path: qm.path, Version: lastCompatible})
   606  				if err != nil {
   607  					return nil, nil, err
   608  				}
   609  				if ok {
   610  					// The last compatible version has a go.mod file, so that's the
   611  					// highest version we're willing to consider. Don't bother even
   612  					// looking at higher versions, because they're all +incompatible from
   613  					// here onward.
   614  					break
   615  				}
   616  
   617  				// No acceptable compatible release has a go.mod file, so the versioning
   618  				// for the module might not be module-aware, and we should respect
   619  				// legacy major-version tags.
   620  				needIncompatible = true
   621  			}
   622  		}
   623  
   624  		if gover.ModIsPrerelease(qm.path, v) {
   625  			prereleases = append(prereleases, v)
   626  		} else {
   627  			releases = append(releases, v)
   628  		}
   629  	}
   630  
   631  	return releases, prereleases, nil
   632  }
   633  
   634  type QueryResult struct {
   635  	Mod      module.Version
   636  	Rev      *modfetch.RevInfo
   637  	Packages []string
   638  }
   639  
   640  // QueryPackages is like QueryPattern, but requires that the pattern match at
   641  // least one package and omits the non-package result (if any).
   642  func QueryPackages(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) {
   643  	pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)
   644  
   645  	if len(pkgMods) == 0 && err == nil {
   646  		replacement := Replacement(modOnly.Mod)
   647  		return nil, &PackageNotInModuleError{
   648  			Mod:         modOnly.Mod,
   649  			Replacement: replacement,
   650  			Query:       query,
   651  			Pattern:     pattern,
   652  		}
   653  	}
   654  
   655  	return pkgMods, err
   656  }
   657  
   658  // QueryPattern looks up the module(s) containing at least one package matching
   659  // the given pattern at the given version. The results are sorted by module path
   660  // length in descending order. If any proxy provides a non-empty set of candidate
   661  // modules, no further proxies are tried.
   662  //
   663  // For wildcard patterns, QueryPattern looks in modules with package paths up to
   664  // the first "..." in the pattern. For the pattern "example.com/a/b.../c",
   665  // QueryPattern would consider prefixes of "example.com/a".
   666  //
   667  // If any matching package is in the main module, QueryPattern considers only
   668  // the main module and only the version "latest", without checking for other
   669  // possible modules.
   670  //
   671  // QueryPattern always returns at least one QueryResult (which may be only
   672  // modOnly) or a non-nil error.
   673  func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) (pkgMods []QueryResult, modOnly *QueryResult, err error) {
   674  	ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query)
   675  	defer span.Done()
   676  
   677  	base := pattern
   678  
   679  	firstError := func(m *search.Match) error {
   680  		if len(m.Errs) == 0 {
   681  			return nil
   682  		}
   683  		return m.Errs[0]
   684  	}
   685  
   686  	var match func(mod module.Version, roots []string, isLocal bool) *search.Match
   687  	matchPattern := pkgpattern.MatchPattern(pattern)
   688  
   689  	if i := strings.Index(pattern, "..."); i >= 0 {
   690  		base = pathpkg.Dir(pattern[:i+3])
   691  		if base == "." {
   692  			return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query}
   693  		}
   694  		match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
   695  			m := search.NewMatch(pattern)
   696  			matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod})
   697  			return m
   698  		}
   699  	} else {
   700  		match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
   701  			m := search.NewMatch(pattern)
   702  			prefix := mod.Path
   703  			if MainModules.Contains(mod.Path) {
   704  				prefix = MainModules.PathPrefix(module.Version{Path: mod.Path})
   705  			}
   706  			for _, root := range roots {
   707  				if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
   708  					m.AddError(err)
   709  				} else if ok {
   710  					m.Pkgs = []string{pattern}
   711  				}
   712  			}
   713  			return m
   714  		}
   715  	}
   716  
   717  	var mainModuleMatches []module.Version
   718  	for _, mainModule := range MainModules.Versions() {
   719  		m := match(mainModule, modRoots, true)
   720  		if len(m.Pkgs) > 0 {
   721  			if query != "upgrade" && query != "patch" {
   722  				return nil, nil, &QueryMatchesPackagesInMainModuleError{
   723  					Pattern:  pattern,
   724  					Query:    query,
   725  					Packages: m.Pkgs,
   726  				}
   727  			}
   728  			if err := allowed(ctx, mainModule); err != nil {
   729  				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)
   730  			}
   731  			return []QueryResult{{
   732  				Mod:      mainModule,
   733  				Rev:      &modfetch.RevInfo{Version: mainModule.Version},
   734  				Packages: m.Pkgs,
   735  			}}, nil, nil
   736  		}
   737  		if err := firstError(m); err != nil {
   738  			return nil, nil, err
   739  		}
   740  
   741  		var matchesMainModule bool
   742  		if matchPattern(mainModule.Path) {
   743  			mainModuleMatches = append(mainModuleMatches, mainModule)
   744  			matchesMainModule = true
   745  		}
   746  
   747  		if (query == "upgrade" || query == "patch") && matchesMainModule {
   748  			if err := allowed(ctx, mainModule); err == nil {
   749  				modOnly = &QueryResult{
   750  					Mod: mainModule,
   751  					Rev: &modfetch.RevInfo{Version: mainModule.Version},
   752  				}
   753  			}
   754  		}
   755  	}
   756  
   757  	var (
   758  		results          []QueryResult
   759  		candidateModules = modulePrefixesExcludingTarget(base)
   760  	)
   761  	if len(candidateModules) == 0 {
   762  		if modOnly != nil {
   763  			return nil, modOnly, nil
   764  		} else if len(mainModuleMatches) != 0 {
   765  			return nil, nil, &QueryMatchesMainModulesError{
   766  				MainModules: mainModuleMatches,
   767  				Pattern:     pattern,
   768  				Query:       query,
   769  			}
   770  		} else {
   771  			return nil, nil, &PackageNotInModuleError{
   772  				MainModules: mainModuleMatches,
   773  				Query:       query,
   774  				Pattern:     pattern,
   775  			}
   776  		}
   777  	}
   778  
   779  	err = modfetch.TryProxies(func(proxy string) error {
   780  		queryModule := func(ctx context.Context, path string) (r QueryResult, err error) {
   781  			ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path)
   782  			defer span.Done()
   783  
   784  			pathCurrent := current(path)
   785  			r.Mod.Path = path
   786  			r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed, nil)
   787  			if err != nil {
   788  				return r, err
   789  			}
   790  			r.Mod.Version = r.Rev.Version
   791  			if gover.IsToolchain(r.Mod.Path) {
   792  				return r, nil
   793  			}
   794  			root, isLocal, err := fetch(ctx, r.Mod)
   795  			if err != nil {
   796  				return r, err
   797  			}
   798  			m := match(r.Mod, []string{root}, isLocal)
   799  			r.Packages = m.Pkgs
   800  			if len(r.Packages) == 0 && !matchPattern(path) {
   801  				if err := firstError(m); err != nil {
   802  					return r, err
   803  				}
   804  				replacement := Replacement(r.Mod)
   805  				return r, &PackageNotInModuleError{
   806  					Mod:         r.Mod,
   807  					Replacement: replacement,
   808  					Query:       query,
   809  					Pattern:     pattern,
   810  				}
   811  			}
   812  			return r, nil
   813  		}
   814  
   815  		allResults, err := queryPrefixModules(ctx, candidateModules, queryModule)
   816  		results = allResults[:0]
   817  		for _, r := range allResults {
   818  			if len(r.Packages) == 0 {
   819  				modOnly = &r
   820  			} else {
   821  				results = append(results, r)
   822  			}
   823  		}
   824  		return err
   825  	})
   826  
   827  	if len(mainModuleMatches) > 0 && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
   828  		return nil, nil, &QueryMatchesMainModulesError{
   829  			Pattern: pattern,
   830  			Query:   query,
   831  		}
   832  	}
   833  	return slices.Clip(results), modOnly, err
   834  }
   835  
   836  // modulePrefixesExcludingTarget returns all prefixes of path that may plausibly
   837  // exist as a module, excluding targetPrefix but otherwise including path
   838  // itself, sorted by descending length. Prefixes that are not valid module paths
   839  // but are valid package paths (like "m" or "example.com/.gen") are included,
   840  // since they might be replaced.
   841  func modulePrefixesExcludingTarget(path string) []string {
   842  	prefixes := make([]string, 0, strings.Count(path, "/")+1)
   843  
   844  	mainModulePrefixes := make(map[string]bool)
   845  	for _, m := range MainModules.Versions() {
   846  		mainModulePrefixes[m.Path] = true
   847  	}
   848  
   849  	for {
   850  		if !mainModulePrefixes[path] {
   851  			if _, _, ok := module.SplitPathVersion(path); ok {
   852  				prefixes = append(prefixes, path)
   853  			}
   854  		}
   855  
   856  		j := strings.LastIndexByte(path, '/')
   857  		if j < 0 {
   858  			break
   859  		}
   860  		path = path[:j]
   861  	}
   862  
   863  	return prefixes
   864  }
   865  
   866  func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) {
   867  	ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules")
   868  	defer span.Done()
   869  
   870  	// If the path we're attempting is not in the module cache and we don't have a
   871  	// fetch result cached either, we'll end up making a (potentially slow)
   872  	// request to the proxy or (often even slower) the origin server.
   873  	// To minimize latency, execute all of those requests in parallel.
   874  	type result struct {
   875  		QueryResult
   876  		err error
   877  	}
   878  	results := make([]result, len(candidateModules))
   879  	var wg sync.WaitGroup
   880  	wg.Add(len(candidateModules))
   881  	for i, p := range candidateModules {
   882  		ctx := trace.StartGoroutine(ctx)
   883  		go func(p string, r *result) {
   884  			r.QueryResult, r.err = queryModule(ctx, p)
   885  			wg.Done()
   886  		}(p, &results[i])
   887  	}
   888  	wg.Wait()
   889  
   890  	// Classify the results. In case of failure, identify the error that the user
   891  	// is most likely to find helpful: the most useful class of error at the
   892  	// longest matching path.
   893  	var (
   894  		noPackage      *PackageNotInModuleError
   895  		noVersion      *NoMatchingVersionError
   896  		noPatchBase    *NoPatchBaseError
   897  		invalidPath    *module.InvalidPathError // see comment in case below
   898  		invalidVersion error
   899  		notExistErr    error
   900  	)
   901  	for _, r := range results {
   902  		switch rErr := r.err.(type) {
   903  		case nil:
   904  			found = append(found, r.QueryResult)
   905  		case *PackageNotInModuleError:
   906  			// Given the option, prefer to attribute “package not in module”
   907  			// to modules other than the main one.
   908  			if noPackage == nil || MainModules.Contains(noPackage.Mod.Path) {
   909  				noPackage = rErr
   910  			}
   911  		case *NoMatchingVersionError:
   912  			if noVersion == nil {
   913  				noVersion = rErr
   914  			}
   915  		case *NoPatchBaseError:
   916  			if noPatchBase == nil {
   917  				noPatchBase = rErr
   918  			}
   919  		case *module.InvalidPathError:
   920  			// The prefix was not a valid module path, and there was no replacement.
   921  			// Prefixes like this may appear in candidateModules, since we handle
   922  			// replaced modules that weren't required in the repo lookup process
   923  			// (see lookupRepo).
   924  			//
   925  			// A shorter prefix may be a valid module path and may contain a valid
   926  			// import path, so this is a low-priority error.
   927  			if invalidPath == nil {
   928  				invalidPath = rErr
   929  			}
   930  		default:
   931  			if errors.Is(rErr, fs.ErrNotExist) {
   932  				if notExistErr == nil {
   933  					notExistErr = rErr
   934  				}
   935  			} else if iv := (*module.InvalidVersionError)(nil); errors.As(rErr, &iv) {
   936  				if invalidVersion == nil {
   937  					invalidVersion = rErr
   938  				}
   939  			} else if err == nil {
   940  				if len(found) > 0 || noPackage != nil {
   941  					// golang.org/issue/34094: If we have already found a module that
   942  					// could potentially contain the target package, ignore unclassified
   943  					// errors for modules with shorter paths.
   944  
   945  					// golang.org/issue/34383 is a special case of this: if we have
   946  					// already found example.com/foo/v2@v2.0.0 with a matching go.mod
   947  					// file, ignore the error from example.com/foo@v2.0.0.
   948  				} else {
   949  					err = r.err
   950  				}
   951  			}
   952  		}
   953  	}
   954  
   955  	// TODO(#26232): If len(found) == 0 and some of the errors are 4xx HTTP
   956  	// codes, have the auth package recheck the failed paths.
   957  	// If we obtain new credentials for any of them, re-run the above loop.
   958  
   959  	if len(found) == 0 && err == nil {
   960  		switch {
   961  		case noPackage != nil:
   962  			err = noPackage
   963  		case noVersion != nil:
   964  			err = noVersion
   965  		case noPatchBase != nil:
   966  			err = noPatchBase
   967  		case invalidPath != nil:
   968  			err = invalidPath
   969  		case invalidVersion != nil:
   970  			err = invalidVersion
   971  		case notExistErr != nil:
   972  			err = notExistErr
   973  		default:
   974  			panic("queryPrefixModules: no modules found, but no error detected")
   975  		}
   976  	}
   977  
   978  	return found, err
   979  }
   980  
   981  // A NoMatchingVersionError indicates that Query found a module at the requested
   982  // path, but not at any versions satisfying the query string and allow-function.
   983  //
   984  // NOTE: NoMatchingVersionError MUST NOT implement Is(fs.ErrNotExist).
   985  //
   986  // If the module came from a proxy, that proxy had to return a successful status
   987  // code for the versions it knows about, and thus did not have the opportunity
   988  // to return a non-400 status code to suppress fallback.
   989  type NoMatchingVersionError struct {
   990  	query, current string
   991  }
   992  
   993  func (e *NoMatchingVersionError) Error() string {
   994  	currentSuffix := ""
   995  	if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" {
   996  		currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
   997  	}
   998  	return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
   999  }
  1000  
  1001  // A NoPatchBaseError indicates that Query was called with the query "patch"
  1002  // but with a current version of "" or "none".
  1003  type NoPatchBaseError struct {
  1004  	path string
  1005  }
  1006  
  1007  func (e *NoPatchBaseError) Error() string {
  1008  	return fmt.Sprintf(`can't query version "patch" of module %s: no existing version is required`, e.path)
  1009  }
  1010  
  1011  // A WildcardInFirstElementError indicates that a pattern passed to QueryPattern
  1012  // had a wildcard in its first path element, and therefore had no pattern-prefix
  1013  // modules to search in.
  1014  type WildcardInFirstElementError struct {
  1015  	Pattern string
  1016  	Query   string
  1017  }
  1018  
  1019  func (e *WildcardInFirstElementError) Error() string {
  1020  	return fmt.Sprintf("no modules to query for %s@%s because first path element contains a wildcard", e.Pattern, e.Query)
  1021  }
  1022  
  1023  // A PackageNotInModuleError indicates that QueryPattern found a candidate
  1024  // module at the requested version, but that module did not contain any packages
  1025  // matching the requested pattern.
  1026  //
  1027  // NOTE: PackageNotInModuleError MUST NOT implement Is(fs.ErrNotExist).
  1028  //
  1029  // If the module came from a proxy, that proxy had to return a successful status
  1030  // code for the versions it knows about, and thus did not have the opportunity
  1031  // to return a non-400 status code to suppress fallback.
  1032  type PackageNotInModuleError struct {
  1033  	MainModules []module.Version
  1034  	Mod         module.Version
  1035  	Replacement module.Version
  1036  	Query       string
  1037  	Pattern     string
  1038  }
  1039  
  1040  func (e *PackageNotInModuleError) Error() string {
  1041  	if len(e.MainModules) > 0 {
  1042  		prefix := "workspace modules do"
  1043  		if len(e.MainModules) == 1 {
  1044  			prefix = fmt.Sprintf("main module (%s) does", e.MainModules[0])
  1045  		}
  1046  		if strings.Contains(e.Pattern, "...") {
  1047  			return fmt.Sprintf("%s not contain packages matching %s", prefix, e.Pattern)
  1048  		}
  1049  		return fmt.Sprintf("%s not contain package %s", prefix, e.Pattern)
  1050  	}
  1051  
  1052  	found := ""
  1053  	if r := e.Replacement; r.Path != "" {
  1054  		replacement := r.Path
  1055  		if r.Version != "" {
  1056  			replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
  1057  		}
  1058  		if e.Query == e.Mod.Version {
  1059  			found = fmt.Sprintf(" (replaced by %s)", replacement)
  1060  		} else {
  1061  			found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
  1062  		}
  1063  	} else if e.Query != e.Mod.Version {
  1064  		found = fmt.Sprintf(" (%s)", e.Mod.Version)
  1065  	}
  1066  
  1067  	if strings.Contains(e.Pattern, "...") {
  1068  		return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
  1069  	}
  1070  	return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
  1071  }
  1072  
  1073  func (e *PackageNotInModuleError) ImportPath() string {
  1074  	if !strings.Contains(e.Pattern, "...") {
  1075  		return e.Pattern
  1076  	}
  1077  	return ""
  1078  }
  1079  
  1080  // versionHasGoMod returns whether a version has a go.mod file.
  1081  //
  1082  // versionHasGoMod fetches the go.mod file (possibly a fake) and true if it
  1083  // contains anything other than a module directive with the same path. When a
  1084  // module does not have a real go.mod file, the go command acts as if it had one
  1085  // that only contained a module directive. Normal go.mod files created after
  1086  // 1.12 at least have a go directive.
  1087  //
  1088  // This function is a heuristic, since it's possible to commit a file that would
  1089  // pass this test. However, we only need a heuristic for determining whether
  1090  // +incompatible versions may be "latest", which is what this function is used
  1091  // for.
  1092  //
  1093  // This heuristic is useful for two reasons: first, when using a proxy,
  1094  // this lets us fetch from the .mod endpoint which is much faster than the .zip
  1095  // endpoint. The .mod file is used anyway, even if the .zip file contains a
  1096  // go.mod with different content. Second, if we don't fetch the .zip, then
  1097  // we don't need to verify it in go.sum. This makes 'go list -m -u' faster
  1098  // and simpler.
  1099  func versionHasGoMod(_ context.Context, m module.Version) (bool, error) {
  1100  	_, data, err := rawGoModData(m)
  1101  	if err != nil {
  1102  		return false, err
  1103  	}
  1104  	isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path))
  1105  	return !isFake, nil
  1106  }
  1107  
  1108  // A versionRepo is a subset of modfetch.Repo that can report information about
  1109  // available versions, but cannot fetch specific source files.
  1110  type versionRepo interface {
  1111  	ModulePath() string
  1112  	CheckReuse(context.Context, *codehost.Origin) error
  1113  	Versions(ctx context.Context, prefix string) (*modfetch.Versions, error)
  1114  	Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error)
  1115  	Latest(context.Context) (*modfetch.RevInfo, error)
  1116  }
  1117  
  1118  var _ versionRepo = modfetch.Repo(nil)
  1119  
  1120  func lookupRepo(ctx context.Context, proxy, path string) (repo versionRepo, err error) {
  1121  	if path != "go" && path != "toolchain" {
  1122  		err = module.CheckPath(path)
  1123  	}
  1124  	if err == nil {
  1125  		repo = modfetch.Lookup(ctx, proxy, path)
  1126  	} else {
  1127  		repo = emptyRepo{path: path, err: err}
  1128  	}
  1129  
  1130  	if MainModules == nil {
  1131  		return repo, err
  1132  	} else if _, ok := MainModules.HighestReplaced()[path]; ok {
  1133  		return &replacementRepo{repo: repo}, nil
  1134  	}
  1135  
  1136  	return repo, err
  1137  }
  1138  
  1139  // An emptyRepo is a versionRepo that contains no versions.
  1140  type emptyRepo struct {
  1141  	path string
  1142  	err  error
  1143  }
  1144  
  1145  var _ versionRepo = emptyRepo{}
  1146  
  1147  func (er emptyRepo) ModulePath() string { return er.path }
  1148  func (er emptyRepo) CheckReuse(ctx context.Context, old *codehost.Origin) error {
  1149  	return fmt.Errorf("empty repo")
  1150  }
  1151  func (er emptyRepo) Versions(ctx context.Context, prefix string) (*modfetch.Versions, error) {
  1152  	return &modfetch.Versions{}, nil
  1153  }
  1154  func (er emptyRepo) Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error) {
  1155  	return nil, er.err
  1156  }
  1157  func (er emptyRepo) Latest(ctx context.Context) (*modfetch.RevInfo, error) { return nil, er.err }
  1158  
  1159  // A replacementRepo augments a versionRepo to include the replacement versions
  1160  // (if any) found in the main module's go.mod file.
  1161  //
  1162  // A replacementRepo suppresses "not found" errors for otherwise-nonexistent
  1163  // modules, so a replacementRepo should only be constructed for a module that
  1164  // actually has one or more valid replacements.
  1165  type replacementRepo struct {
  1166  	repo versionRepo
  1167  }
  1168  
  1169  var _ versionRepo = (*replacementRepo)(nil)
  1170  
  1171  func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() }
  1172  
  1173  func (rr *replacementRepo) CheckReuse(ctx context.Context, old *codehost.Origin) error {
  1174  	return fmt.Errorf("replacement repo")
  1175  }
  1176  
  1177  // Versions returns the versions from rr.repo augmented with any matching
  1178  // replacement versions.
  1179  func (rr *replacementRepo) Versions(ctx context.Context, prefix string) (*modfetch.Versions, error) {
  1180  	repoVersions, err := rr.repo.Versions(ctx, prefix)
  1181  	if err != nil {
  1182  		if !errors.Is(err, os.ErrNotExist) {
  1183  			return nil, err
  1184  		}
  1185  		repoVersions = new(modfetch.Versions)
  1186  	}
  1187  
  1188  	versions := repoVersions.List
  1189  	for _, mm := range MainModules.Versions() {
  1190  		if index := MainModules.Index(mm); index != nil && len(index.replace) > 0 {
  1191  			path := rr.ModulePath()
  1192  			for m := range index.replace {
  1193  				if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) {
  1194  					versions = append(versions, m.Version)
  1195  				}
  1196  			}
  1197  		}
  1198  	}
  1199  
  1200  	if len(versions) == len(repoVersions.List) { // replacement versions added
  1201  		return repoVersions, nil
  1202  	}
  1203  
  1204  	path := rr.ModulePath()
  1205  	sort.Slice(versions, func(i, j int) bool {
  1206  		return gover.ModCompare(path, versions[i], versions[j]) < 0
  1207  	})
  1208  	str.Uniq(&versions)
  1209  	return &modfetch.Versions{List: versions}, nil
  1210  }
  1211  
  1212  func (rr *replacementRepo) Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error) {
  1213  	info, err := rr.repo.Stat(ctx, rev)
  1214  	if err == nil {
  1215  		return info, err
  1216  	}
  1217  	var hasReplacements bool
  1218  	for _, v := range MainModules.Versions() {
  1219  		if index := MainModules.Index(v); index != nil && len(index.replace) > 0 {
  1220  			hasReplacements = true
  1221  		}
  1222  	}
  1223  	if !hasReplacements {
  1224  		return info, err
  1225  	}
  1226  
  1227  	v := module.CanonicalVersion(rev)
  1228  	if v != rev {
  1229  		// The replacements in the go.mod file list only canonical semantic versions,
  1230  		// so a non-canonical version can't possibly have a replacement.
  1231  		return info, err
  1232  	}
  1233  
  1234  	path := rr.ModulePath()
  1235  	_, pathMajor, ok := module.SplitPathVersion(path)
  1236  	if ok && pathMajor == "" {
  1237  		if err := module.CheckPathMajor(v, pathMajor); err != nil && semver.Build(v) == "" {
  1238  			v += "+incompatible"
  1239  		}
  1240  	}
  1241  
  1242  	if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
  1243  		return info, err
  1244  	}
  1245  	return rr.replacementStat(v)
  1246  }
  1247  
  1248  func (rr *replacementRepo) Latest(ctx context.Context) (*modfetch.RevInfo, error) {
  1249  	info, err := rr.repo.Latest(ctx)
  1250  	path := rr.ModulePath()
  1251  
  1252  	if v, ok := MainModules.HighestReplaced()[path]; ok {
  1253  		if v == "" {
  1254  			// The only replacement is a wildcard that doesn't specify a version, so
  1255  			// synthesize a pseudo-version with an appropriate major version and a
  1256  			// timestamp below any real timestamp. That way, if the main module is
  1257  			// used from within some other module, the user will be able to upgrade
  1258  			// the requirement to any real version they choose.
  1259  			if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 {
  1260  				v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
  1261  			} else {
  1262  				v = module.PseudoVersion("v0", "", time.Time{}, "000000000000")
  1263  			}
  1264  		}
  1265  
  1266  		if err != nil || gover.ModCompare(path, v, info.Version) > 0 {
  1267  			return rr.replacementStat(v)
  1268  		}
  1269  	}
  1270  
  1271  	return info, err
  1272  }
  1273  
  1274  func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) {
  1275  	rev := &modfetch.RevInfo{Version: v}
  1276  	if module.IsPseudoVersion(v) {
  1277  		rev.Time, _ = module.PseudoVersionTime(v)
  1278  		rev.Short, _ = module.PseudoVersionRev(v)
  1279  	}
  1280  	return rev, nil
  1281  }
  1282  
  1283  // A QueryMatchesMainModulesError indicates that a query requests
  1284  // a version of the main module that cannot be satisfied.
  1285  // (The main module's version cannot be changed.)
  1286  type QueryMatchesMainModulesError struct {
  1287  	MainModules []module.Version
  1288  	Pattern     string
  1289  	Query       string
  1290  }
  1291  
  1292  func (e *QueryMatchesMainModulesError) Error() string {
  1293  	if MainModules.Contains(e.Pattern) {
  1294  		return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern)
  1295  	}
  1296  
  1297  	plural := ""
  1298  	mainModulePaths := make([]string, len(e.MainModules))
  1299  	for i := range e.MainModules {
  1300  		mainModulePaths[i] = e.MainModules[i].Path
  1301  	}
  1302  	if len(e.MainModules) > 1 {
  1303  		plural = "s"
  1304  	}
  1305  	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, ", "))
  1306  }
  1307  
  1308  // A QueryUpgradesAllError indicates that a query requests
  1309  // an upgrade on the all pattern.
  1310  // (The main module's version cannot be changed.)
  1311  type QueryUpgradesAllError struct {
  1312  	MainModules []module.Version
  1313  	Query       string
  1314  }
  1315  
  1316  func (e *QueryUpgradesAllError) Error() string {
  1317  	var plural string = ""
  1318  	if len(e.MainModules) != 1 {
  1319  		plural = "s"
  1320  	}
  1321  
  1322  	return fmt.Sprintf("can't request version %q of pattern \"all\" that includes the main module%s", e.Query, plural)
  1323  }
  1324  
  1325  // A QueryMatchesPackagesInMainModuleError indicates that a query cannot be
  1326  // satisfied because it matches one or more packages found in the main module.
  1327  type QueryMatchesPackagesInMainModuleError struct {
  1328  	Pattern  string
  1329  	Query    string
  1330  	Packages []string
  1331  }
  1332  
  1333  func (e *QueryMatchesPackagesInMainModuleError) Error() string {
  1334  	if len(e.Packages) > 1 {
  1335  		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)
  1336  	}
  1337  
  1338  	if search.IsMetaPackage(e.Pattern) || strings.Contains(e.Pattern, "...") {
  1339  		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)
  1340  	}
  1341  
  1342  	return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query)
  1343  }
  1344  

View as plain text