github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/modload/modfile.go (about)

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