Black Lives Matter. Support the Equal Justice Initiative.

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

View as plain text