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

View as plain text