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