Black Lives Matter. Support the Equal Justice Initiative.

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

Documentation: cmd/go/internal/modload

     1  // Copyright 2020 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  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"strings"
    14  	"sync"
    15  	"unicode"
    16  
    17  	"cmd/go/internal/base"
    18  	"cmd/go/internal/cfg"
    19  	"cmd/go/internal/fsys"
    20  	"cmd/go/internal/lockedfile"
    21  	"cmd/go/internal/modfetch"
    22  	"cmd/go/internal/par"
    23  	"cmd/go/internal/trace"
    24  
    25  	"golang.org/x/mod/modfile"
    26  	"golang.org/x/mod/module"
    27  	"golang.org/x/mod/semver"
    28  )
    29  
    30  const (
    31  	// narrowAllVersionV is the Go version (plus leading "v") at which the
    32  	// module-module "all" pattern no longer closes over the dependencies of
    33  	// tests outside of the main module.
    34  	narrowAllVersionV = "v1.16"
    35  
    36  	// lazyLoadingVersionV is the Go version (plus leading "v") at which a
    37  	// module's go.mod file is expected to list explicit requirements on every
    38  	// module that provides any package transitively imported by that module.
    39  	lazyLoadingVersionV = "v1.17"
    40  
    41  	// separateIndirectVersionV is the Go version (plus leading "v") at which
    42  	// "// indirect" dependencies are added in a block separate from the direct
    43  	// ones. See https://golang.org/issue/45965.
    44  	separateIndirectVersionV = "v1.17"
    45  )
    46  
    47  const (
    48  	// go117EnableLazyLoading toggles whether lazy-loading code paths should be
    49  	// active. It will be removed once the lazy loading implementation is stable
    50  	// and well-tested.
    51  	go117EnableLazyLoading = true
    52  
    53  	// go1117LazyTODO is a constant that exists only until lazy loading is
    54  	// implemented. Its use indicates a condition that will need to change if the
    55  	// main module is lazy.
    56  	go117LazyTODO = false
    57  )
    58  
    59  var modFile *modfile.File
    60  
    61  // modFileGoVersion returns the (non-empty) Go version at which the requirements
    62  // in modFile are intepreted, or the latest Go version if modFile is nil.
    63  func modFileGoVersion() string {
    64  	if modFile == nil {
    65  		return LatestGoVersion()
    66  	}
    67  	if modFile.Go == nil || modFile.Go.Version == "" {
    68  		// The main module necessarily has a go.mod file, and that file lacks a
    69  		// 'go' directive. The 'go' command has been adding that directive
    70  		// automatically since Go 1.12, so this module either dates to Go 1.11 or
    71  		// has been erroneously hand-edited.
    72  		//
    73  		// The semantics of the go.mod file are more-or-less the same from Go 1.11
    74  		// through Go 1.16, changing at 1.17 for lazy loading. So even though a
    75  		// go.mod file without a 'go' directive is theoretically a Go 1.11 file,
    76  		// scripts may assume that it ends up as a Go 1.16 module.
    77  		return "1.16"
    78  	}
    79  	return modFile.Go.Version
    80  }
    81  
    82  // A modFileIndex is an index of data corresponding to a modFile
    83  // at a specific point in time.
    84  type modFileIndex struct {
    85  	data            []byte
    86  	dataNeedsFix    bool // true if fixVersion applied a change while parsing data
    87  	module          module.Version
    88  	goVersionV      string // GoVersion with "v" prefix
    89  	require         map[module.Version]requireMeta
    90  	replace         map[module.Version]module.Version
    91  	highestReplaced map[string]string // highest replaced version of each module path; empty string for wildcard-only replacements
    92  	exclude         map[module.Version]bool
    93  }
    94  
    95  // index is the index of the go.mod file as of when it was last read or written.
    96  var index *modFileIndex
    97  
    98  type requireMeta struct {
    99  	indirect bool
   100  }
   101  
   102  // A modDepth indicates which dependencies should be loaded for a go.mod file.
   103  type modDepth uint8
   104  
   105  const (
   106  	lazy  modDepth = iota // load dependencies only as needed
   107  	eager                 // load all transitive dependencies eagerly
   108  )
   109  
   110  func modDepthFromGoVersion(goVersion string) modDepth {
   111  	if !go117EnableLazyLoading {
   112  		return eager
   113  	}
   114  	if semver.Compare("v"+goVersion, lazyLoadingVersionV) < 0 {
   115  		return eager
   116  	}
   117  	return lazy
   118  }
   119  
   120  // CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by
   121  // the main module's go.mod or retracted by its author. Most version queries use
   122  // this to filter out versions that should not be used.
   123  func CheckAllowed(ctx context.Context, m module.Version) error {
   124  	if err := CheckExclusions(ctx, m); err != nil {
   125  		return err
   126  	}
   127  	if err := CheckRetractions(ctx, m); err != nil {
   128  		return err
   129  	}
   130  	return nil
   131  }
   132  
   133  // ErrDisallowed is returned by version predicates passed to Query and similar
   134  // functions to indicate that a version should not be considered.
   135  var ErrDisallowed = errors.New("disallowed module version")
   136  
   137  // CheckExclusions returns an error equivalent to ErrDisallowed if module m is
   138  // excluded by the main module's go.mod file.
   139  func CheckExclusions(ctx context.Context, m module.Version) error {
   140  	if index != nil && index.exclude[m] {
   141  		return module.VersionError(m, errExcluded)
   142  	}
   143  	return nil
   144  }
   145  
   146  var errExcluded = &excludedError{}
   147  
   148  type excludedError struct{}
   149  
   150  func (e *excludedError) Error() string     { return "excluded by go.mod" }
   151  func (e *excludedError) Is(err error) bool { return err == ErrDisallowed }
   152  
   153  // CheckRetractions returns an error if module m has been retracted by
   154  // its author.
   155  func CheckRetractions(ctx context.Context, m module.Version) (err error) {
   156  	defer func() {
   157  		if retractErr := (*ModuleRetractedError)(nil); err == nil || errors.As(err, &retractErr) {
   158  			return
   159  		}
   160  		// Attribute the error to the version being checked, not the version from
   161  		// which the retractions were to be loaded.
   162  		if mErr := (*module.ModuleError)(nil); errors.As(err, &mErr) {
   163  			err = mErr.Err
   164  		}
   165  		err = &retractionLoadingError{m: m, err: err}
   166  	}()
   167  
   168  	if m.Version == "" {
   169  		// Main module, standard library, or file replacement module.
   170  		// Cannot be retracted.
   171  		return nil
   172  	}
   173  	if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
   174  		// All versions of the module were replaced.
   175  		// Don't load retractions, since we'd just load the replacement.
   176  		return nil
   177  	}
   178  
   179  	// Find the latest available version of the module, and load its go.mod. If
   180  	// the latest version is replaced, we'll load the replacement.
   181  	//
   182  	// If there's an error loading the go.mod, we'll return it here. These errors
   183  	// should generally be ignored by callers since they happen frequently when
   184  	// we're offline. These errors are not equivalent to ErrDisallowed, so they
   185  	// may be distinguished from retraction errors.
   186  	//
   187  	// We load the raw file here: the go.mod file may have a different module
   188  	// path that we expect if the module or its repository was renamed.
   189  	// We still want to apply retractions to other aliases of the module.
   190  	rm, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
   191  	if err != nil {
   192  		return err
   193  	}
   194  	summary, err := rawGoModSummary(rm)
   195  	if err != nil {
   196  		return err
   197  	}
   198  
   199  	var rationale []string
   200  	isRetracted := false
   201  	for _, r := range summary.retract {
   202  		if semver.Compare(r.Low, m.Version) <= 0 && semver.Compare(m.Version, r.High) <= 0 {
   203  			isRetracted = true
   204  			if r.Rationale != "" {
   205  				rationale = append(rationale, r.Rationale)
   206  			}
   207  		}
   208  	}
   209  	if isRetracted {
   210  		return module.VersionError(m, &ModuleRetractedError{Rationale: rationale})
   211  	}
   212  	return nil
   213  }
   214  
   215  type ModuleRetractedError struct {
   216  	Rationale []string
   217  }
   218  
   219  func (e *ModuleRetractedError) Error() string {
   220  	msg := "retracted by module author"
   221  	if len(e.Rationale) > 0 {
   222  		// This is meant to be a short error printed on a terminal, so just
   223  		// print the first rationale.
   224  		msg += ": " + ShortMessage(e.Rationale[0], "retracted by module author")
   225  	}
   226  	return msg
   227  }
   228  
   229  func (e *ModuleRetractedError) Is(err error) bool {
   230  	return err == ErrDisallowed
   231  }
   232  
   233  type retractionLoadingError struct {
   234  	m   module.Version
   235  	err error
   236  }
   237  
   238  func (e *retractionLoadingError) Error() string {
   239  	return fmt.Sprintf("loading module retractions for %v: %v", e.m, e.err)
   240  }
   241  
   242  func (e *retractionLoadingError) Unwrap() error {
   243  	return e.err
   244  }
   245  
   246  // ShortMessage returns a string from go.mod (for example, a retraction
   247  // rationale or deprecation message) that is safe to print in a terminal.
   248  //
   249  // If the given string is empty, ShortMessage returns the given default. If the
   250  // given string is too long or contains non-printable characters, ShortMessage
   251  // returns a hard-coded string.
   252  func ShortMessage(message, emptyDefault string) string {
   253  	const maxLen = 500
   254  	if i := strings.Index(message, "\n"); i >= 0 {
   255  		message = message[:i]
   256  	}
   257  	message = strings.TrimSpace(message)
   258  	if message == "" {
   259  		return emptyDefault
   260  	}
   261  	if len(message) > maxLen {
   262  		return "(message omitted: too long)"
   263  	}
   264  	for _, r := range message {
   265  		if !unicode.IsGraphic(r) && !unicode.IsSpace(r) {
   266  			return "(message omitted: contains non-printable characters)"
   267  		}
   268  	}
   269  	// NOTE: the go.mod parser rejects invalid UTF-8, so we don't check that here.
   270  	return message
   271  }
   272  
   273  // CheckDeprecation returns a deprecation message from the go.mod file of the
   274  // latest version of the given module. Deprecation messages are comments
   275  // before or on the same line as the module directives that start with
   276  // "Deprecated:" and run until the end of the paragraph.
   277  //
   278  // CheckDeprecation returns an error if the message can't be loaded.
   279  // CheckDeprecation returns "", nil if there is no deprecation message.
   280  func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string, err error) {
   281  	defer func() {
   282  		if err != nil {
   283  			err = fmt.Errorf("loading deprecation for %s: %w", m.Path, err)
   284  		}
   285  	}()
   286  
   287  	if m.Version == "" {
   288  		// Main module, standard library, or file replacement module.
   289  		// Don't look up deprecation.
   290  		return "", nil
   291  	}
   292  	if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
   293  		// All versions of the module were replaced.
   294  		// We'll look up deprecation separately for the replacement.
   295  		return "", nil
   296  	}
   297  
   298  	latest, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
   299  	if err != nil {
   300  		return "", err
   301  	}
   302  	summary, err := rawGoModSummary(latest)
   303  	if err != nil {
   304  		return "", err
   305  	}
   306  	return summary.deprecated, nil
   307  }
   308  
   309  // Replacement returns the replacement for mod, if any, from go.mod.
   310  // If there is no replacement for mod, Replacement returns
   311  // a module.Version with Path == "".
   312  func Replacement(mod module.Version) module.Version {
   313  	if index != nil {
   314  		if r, ok := index.replace[mod]; ok {
   315  			return r
   316  		}
   317  		if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
   318  			return r
   319  		}
   320  	}
   321  	return module.Version{}
   322  }
   323  
   324  // resolveReplacement returns the module actually used to load the source code
   325  // for m: either m itself, or the replacement for m (iff m is replaced).
   326  func resolveReplacement(m module.Version) module.Version {
   327  	if r := Replacement(m); r.Path != "" {
   328  		return r
   329  	}
   330  	return m
   331  }
   332  
   333  // indexModFile rebuilds the index of modFile.
   334  // If modFile has been changed since it was first read,
   335  // modFile.Cleanup must be called before indexModFile.
   336  func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex {
   337  	i := new(modFileIndex)
   338  	i.data = data
   339  	i.dataNeedsFix = needsFix
   340  
   341  	i.module = module.Version{}
   342  	if modFile.Module != nil {
   343  		i.module = modFile.Module.Mod
   344  	}
   345  
   346  	i.goVersionV = ""
   347  	if modFile.Go == nil {
   348  		rawGoVersion.Store(Target, "")
   349  	} else {
   350  		// We're going to use the semver package to compare Go versions, so go ahead
   351  		// and add the "v" prefix it expects once instead of every time.
   352  		i.goVersionV = "v" + modFile.Go.Version
   353  		rawGoVersion.Store(Target, modFile.Go.Version)
   354  	}
   355  
   356  	i.require = make(map[module.Version]requireMeta, len(modFile.Require))
   357  	for _, r := range modFile.Require {
   358  		i.require[r.Mod] = requireMeta{indirect: r.Indirect}
   359  	}
   360  
   361  	i.replace = make(map[module.Version]module.Version, len(modFile.Replace))
   362  	for _, r := range modFile.Replace {
   363  		if prev, dup := i.replace[r.Old]; dup && prev != r.New {
   364  			base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New)
   365  		}
   366  		i.replace[r.Old] = r.New
   367  	}
   368  
   369  	i.highestReplaced = make(map[string]string)
   370  	for _, r := range modFile.Replace {
   371  		v, ok := i.highestReplaced[r.Old.Path]
   372  		if !ok || semver.Compare(r.Old.Version, v) > 0 {
   373  			i.highestReplaced[r.Old.Path] = r.Old.Version
   374  		}
   375  	}
   376  
   377  	i.exclude = make(map[module.Version]bool, len(modFile.Exclude))
   378  	for _, x := range modFile.Exclude {
   379  		i.exclude[x.Mod] = true
   380  	}
   381  
   382  	return i
   383  }
   384  
   385  // modFileIsDirty reports whether the go.mod file differs meaningfully
   386  // from what was indexed.
   387  // If modFile has been changed (even cosmetically) since it was first read,
   388  // modFile.Cleanup must be called before modFileIsDirty.
   389  func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
   390  	if i == nil {
   391  		return modFile != nil
   392  	}
   393  
   394  	if i.dataNeedsFix {
   395  		return true
   396  	}
   397  
   398  	if modFile.Module == nil {
   399  		if i.module != (module.Version{}) {
   400  			return true
   401  		}
   402  	} else if modFile.Module.Mod != i.module {
   403  		return true
   404  	}
   405  
   406  	if modFile.Go == nil {
   407  		if i.goVersionV != "" {
   408  			return true
   409  		}
   410  	} else if "v"+modFile.Go.Version != i.goVersionV {
   411  		if i.goVersionV == "" && cfg.BuildMod != "mod" {
   412  			// go.mod files did not always require a 'go' version, so do not error out
   413  			// if one is missing — we may be inside an older module in the module
   414  			// cache, and should bias toward providing useful behavior.
   415  		} else {
   416  			return true
   417  		}
   418  	}
   419  
   420  	if len(modFile.Require) != len(i.require) ||
   421  		len(modFile.Replace) != len(i.replace) ||
   422  		len(modFile.Exclude) != len(i.exclude) {
   423  		return true
   424  	}
   425  
   426  	for _, r := range modFile.Require {
   427  		if meta, ok := i.require[r.Mod]; !ok {
   428  			return true
   429  		} else if r.Indirect != meta.indirect {
   430  			if cfg.BuildMod == "readonly" {
   431  				// The module's requirements are consistent; only the "// indirect"
   432  				// comments that are wrong. But those are only guaranteed to be accurate
   433  				// after a "go mod tidy" — it's a good idea to run those before
   434  				// committing a change, but it's certainly not mandatory.
   435  			} else {
   436  				return true
   437  			}
   438  		}
   439  	}
   440  
   441  	for _, r := range modFile.Replace {
   442  		if r.New != i.replace[r.Old] {
   443  			return true
   444  		}
   445  	}
   446  
   447  	for _, x := range modFile.Exclude {
   448  		if !i.exclude[x.Mod] {
   449  			return true
   450  		}
   451  	}
   452  
   453  	return false
   454  }
   455  
   456  // rawGoVersion records the Go version parsed from each module's go.mod file.
   457  //
   458  // If a module is replaced, the version of the replacement is keyed by the
   459  // replacement module.Version, not the version being replaced.
   460  var rawGoVersion sync.Map // map[module.Version]string
   461  
   462  // A modFileSummary is a summary of a go.mod file for which we do not need to
   463  // retain complete information — for example, the go.mod file of a dependency
   464  // module.
   465  type modFileSummary struct {
   466  	module     module.Version
   467  	goVersion  string
   468  	depth      modDepth
   469  	require    []module.Version
   470  	retract    []retraction
   471  	deprecated string
   472  }
   473  
   474  // A retraction consists of a retracted version interval and rationale.
   475  // retraction is like modfile.Retract, but it doesn't point to the syntax tree.
   476  type retraction struct {
   477  	modfile.VersionInterval
   478  	Rationale string
   479  }
   480  
   481  // goModSummary returns a summary of the go.mod file for module m,
   482  // taking into account any replacements for m, exclusions of its dependencies,
   483  // and/or vendoring.
   484  //
   485  // m must be a version in the module graph, reachable from the Target module.
   486  // In readonly mode, the go.sum file must contain an entry for m's go.mod file
   487  // (or its replacement). goModSummary must not be called for the Target module
   488  // itself, as its requirements may change. Use rawGoModSummary for other
   489  // module versions.
   490  //
   491  // The caller must not modify the returned summary.
   492  func goModSummary(m module.Version) (*modFileSummary, error) {
   493  	if m == Target {
   494  		panic("internal error: goModSummary called on the Target module")
   495  	}
   496  
   497  	if cfg.BuildMod == "vendor" {
   498  		summary := &modFileSummary{
   499  			module: module.Version{Path: m.Path},
   500  		}
   501  		if vendorVersion[m.Path] != m.Version {
   502  			// This module is not vendored, so packages cannot be loaded from it and
   503  			// it cannot be relevant to the build.
   504  			return summary, nil
   505  		}
   506  
   507  		// For every module other than the target,
   508  		// return the full list of modules from modules.txt.
   509  		readVendorList()
   510  
   511  		// We don't know what versions the vendored module actually relies on,
   512  		// so assume that it requires everything.
   513  		summary.require = vendorList
   514  		return summary, nil
   515  	}
   516  
   517  	actual := resolveReplacement(m)
   518  	if HasModRoot() && cfg.BuildMod == "readonly" && actual.Version != "" {
   519  		key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"}
   520  		if !modfetch.HaveSum(key) {
   521  			suggestion := fmt.Sprintf("; to add it:\n\tgo mod download %s", m.Path)
   522  			return nil, module.VersionError(actual, &sumMissingError{suggestion: suggestion})
   523  		}
   524  	}
   525  	summary, err := rawGoModSummary(actual)
   526  	if err != nil {
   527  		return nil, err
   528  	}
   529  
   530  	if actual.Version == "" {
   531  		// The actual module is a filesystem-local replacement, for which we have
   532  		// unfortunately not enforced any sort of invariants about module lines or
   533  		// matching module paths. Anything goes.
   534  		//
   535  		// TODO(bcmills): Remove this special-case, update tests, and add a
   536  		// release note.
   537  	} else {
   538  		if summary.module.Path == "" {
   539  			return nil, module.VersionError(actual, errors.New("parsing go.mod: missing module line"))
   540  		}
   541  
   542  		// In theory we should only allow mpath to be unequal to m.Path here if the
   543  		// version that we fetched lacks an explicit go.mod file: if the go.mod file
   544  		// is explicit, then it should match exactly (to ensure that imports of other
   545  		// packages within the module are interpreted correctly). Unfortunately, we
   546  		// can't determine that information from the module proxy protocol: we'll have
   547  		// to leave that validation for when we load actual packages from within the
   548  		// module.
   549  		if mpath := summary.module.Path; mpath != m.Path && mpath != actual.Path {
   550  			return nil, module.VersionError(actual, fmt.Errorf(`parsing go.mod:
   551  	module declares its path as: %s
   552  	        but was required as: %s`, mpath, m.Path))
   553  		}
   554  	}
   555  
   556  	if index != nil && len(index.exclude) > 0 {
   557  		// Drop any requirements on excluded versions.
   558  		// Don't modify the cached summary though, since we might need the raw
   559  		// summary separately.
   560  		haveExcludedReqs := false
   561  		for _, r := range summary.require {
   562  			if index.exclude[r] {
   563  				haveExcludedReqs = true
   564  				break
   565  			}
   566  		}
   567  		if haveExcludedReqs {
   568  			s := new(modFileSummary)
   569  			*s = *summary
   570  			s.require = make([]module.Version, 0, len(summary.require))
   571  			for _, r := range summary.require {
   572  				if !index.exclude[r] {
   573  					s.require = append(s.require, r)
   574  				}
   575  			}
   576  			summary = s
   577  		}
   578  	}
   579  	return summary, nil
   580  }
   581  
   582  // rawGoModSummary returns a new summary of the go.mod file for module m,
   583  // ignoring all replacements that may apply to m and excludes that may apply to
   584  // its dependencies.
   585  //
   586  // rawGoModSummary cannot be used on the Target module.
   587  func rawGoModSummary(m module.Version) (*modFileSummary, error) {
   588  	if m == Target {
   589  		panic("internal error: rawGoModSummary called on the Target module")
   590  	}
   591  
   592  	type cached struct {
   593  		summary *modFileSummary
   594  		err     error
   595  	}
   596  	c := rawGoModSummaryCache.Do(m, func() interface{} {
   597  		summary := new(modFileSummary)
   598  		name, data, err := rawGoModData(m)
   599  		if err != nil {
   600  			return cached{nil, err}
   601  		}
   602  		f, err := modfile.ParseLax(name, data, nil)
   603  		if err != nil {
   604  			return cached{nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(name), err))}
   605  		}
   606  		if f.Module != nil {
   607  			summary.module = f.Module.Mod
   608  			summary.deprecated = f.Module.Deprecated
   609  		}
   610  		if f.Go != nil && f.Go.Version != "" {
   611  			rawGoVersion.LoadOrStore(m, f.Go.Version)
   612  			summary.goVersion = f.Go.Version
   613  			summary.depth = modDepthFromGoVersion(f.Go.Version)
   614  		} else {
   615  			summary.depth = eager
   616  		}
   617  		if len(f.Require) > 0 {
   618  			summary.require = make([]module.Version, 0, len(f.Require))
   619  			for _, req := range f.Require {
   620  				summary.require = append(summary.require, req.Mod)
   621  			}
   622  		}
   623  		if len(f.Retract) > 0 {
   624  			summary.retract = make([]retraction, 0, len(f.Retract))
   625  			for _, ret := range f.Retract {
   626  				summary.retract = append(summary.retract, retraction{
   627  					VersionInterval: ret.VersionInterval,
   628  					Rationale:       ret.Rationale,
   629  				})
   630  			}
   631  		}
   632  
   633  		return cached{summary, nil}
   634  	}).(cached)
   635  
   636  	return c.summary, c.err
   637  }
   638  
   639  var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result
   640  
   641  // rawGoModData returns the content of the go.mod file for module m, ignoring
   642  // all replacements that may apply to m.
   643  //
   644  // rawGoModData cannot be used on the Target module.
   645  //
   646  // Unlike rawGoModSummary, rawGoModData does not cache its results in memory.
   647  // Use rawGoModSummary instead unless you specifically need these bytes.
   648  func rawGoModData(m module.Version) (name string, data []byte, err error) {
   649  	if m.Version == "" {
   650  		// m is a replacement module with only a file path.
   651  		dir := m.Path
   652  		if !filepath.IsAbs(dir) {
   653  			dir = filepath.Join(ModRoot(), dir)
   654  		}
   655  		name = filepath.Join(dir, "go.mod")
   656  		if gomodActual, ok := fsys.OverlayPath(name); ok {
   657  			// Don't lock go.mod if it's part of the overlay.
   658  			// On Plan 9, locking requires chmod, and we don't want to modify any file
   659  			// in the overlay. See #44700.
   660  			data, err = os.ReadFile(gomodActual)
   661  		} else {
   662  			data, err = lockedfile.Read(gomodActual)
   663  		}
   664  		if err != nil {
   665  			return "", nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(name), err))
   666  		}
   667  	} else {
   668  		if !semver.IsValid(m.Version) {
   669  			// Disallow the broader queries supported by fetch.Lookup.
   670  			base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version)
   671  		}
   672  		name = "go.mod"
   673  		data, err = modfetch.GoMod(m.Path, m.Version)
   674  	}
   675  	return name, data, err
   676  }
   677  
   678  // queryLatestVersionIgnoringRetractions looks up the latest version of the
   679  // module with the given path without considering retracted or excluded
   680  // versions.
   681  //
   682  // If all versions of the module are replaced,
   683  // queryLatestVersionIgnoringRetractions returns the replacement without making
   684  // a query.
   685  //
   686  // If the queried latest version is replaced,
   687  // queryLatestVersionIgnoringRetractions returns the replacement.
   688  func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (latest module.Version, err error) {
   689  	type entry struct {
   690  		latest module.Version
   691  		err    error
   692  	}
   693  	e := latestVersionIgnoringRetractionsCache.Do(path, func() interface{} {
   694  		ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path)
   695  		defer span.Done()
   696  
   697  		if repl := Replacement(module.Version{Path: path}); repl.Path != "" {
   698  			// All versions of the module were replaced.
   699  			// No need to query.
   700  			return &entry{latest: repl}
   701  		}
   702  
   703  		// Find the latest version of the module.
   704  		// Ignore exclusions from the main module's go.mod.
   705  		const ignoreSelected = ""
   706  		var allowAll AllowedFunc
   707  		rev, err := Query(ctx, path, "latest", ignoreSelected, allowAll)
   708  		if err != nil {
   709  			return &entry{err: err}
   710  		}
   711  		latest := module.Version{Path: path, Version: rev.Version}
   712  		if repl := resolveReplacement(latest); repl.Path != "" {
   713  			latest = repl
   714  		}
   715  		return &entry{latest: latest}
   716  	}).(*entry)
   717  	return e.latest, e.err
   718  }
   719  
   720  var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result
   721  

View as plain text