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