1
2
3
4
5 package modload
6
7 import (
8 "bytes"
9 "context"
10 "errors"
11 "fmt"
12 "io/fs"
13 "os"
14 pathpkg "path"
15 "sort"
16 "strings"
17 "sync"
18 "time"
19
20 "cmd/go/internal/cfg"
21 "cmd/go/internal/imports"
22 "cmd/go/internal/modfetch"
23 "cmd/go/internal/search"
24 "cmd/go/internal/str"
25 "cmd/go/internal/trace"
26
27 "golang.org/x/mod/module"
28 "golang.org/x/mod/semver"
29 )
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 func Query(ctx context.Context, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) {
69 var info *modfetch.RevInfo
70 err := modfetch.TryProxies(func(proxy string) (err error) {
71 info, err = queryProxy(ctx, proxy, path, query, current, allowed)
72 return err
73 })
74 return info, err
75 }
76
77
78
79
80
81
82
83
84
85
86 type AllowedFunc func(context.Context, module.Version) error
87
88 var errQueryDisabled error = queryDisabledError{}
89
90 type queryDisabledError struct{}
91
92 func (queryDisabledError) Error() string {
93 if cfg.BuildModReason == "" {
94 return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod)
95 }
96 return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
97 }
98
99 func queryProxy(ctx context.Context, proxy, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) {
100 ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query)
101 defer span.Done()
102
103 if current != "" && current != "none" && !semver.IsValid(current) {
104 return nil, fmt.Errorf("invalid previous version %q", current)
105 }
106 if cfg.BuildMod == "vendor" {
107 return nil, errQueryDisabled
108 }
109 if allowed == nil {
110 allowed = func(context.Context, module.Version) error { return nil }
111 }
112
113 if MainModules.Contains(path) && (query == "upgrade" || query == "patch") {
114 m := module.Version{Path: path}
115 if err := allowed(ctx, m); err != nil {
116 return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err)
117 }
118 return &modfetch.RevInfo{Version: m.Version}, nil
119 }
120
121 if path == "std" || path == "cmd" {
122 return nil, fmt.Errorf("can't query specific version (%q) of standard-library module %q", query, path)
123 }
124
125 repo, err := lookupRepo(proxy, path)
126 if err != nil {
127 return nil, err
128 }
129
130
131
132 qm, err := newQueryMatcher(path, query, current, allowed)
133 if (err == nil && qm.canStat) || err == errRevQuery {
134
135
136
137
138
139
140 info, err := repo.Stat(query)
141 if err != nil {
142 queryErr := err
143
144
145
146 canonicalQuery := module.CanonicalVersion(query)
147 if canonicalQuery != "" && query != canonicalQuery {
148 info, err = repo.Stat(canonicalQuery)
149 if err != nil && !errors.Is(err, fs.ErrNotExist) {
150 return info, err
151 }
152 }
153 if err != nil {
154 return nil, queryErr
155 }
156 }
157 if err := allowed(ctx, module.Version{Path: path, Version: info.Version}); errors.Is(err, ErrDisallowed) {
158 return nil, err
159 }
160 return info, nil
161 } else if err != nil {
162 return nil, err
163 }
164
165
166 versions, err := repo.Versions(qm.prefix)
167 if err != nil {
168 return nil, err
169 }
170 releases, prereleases, err := qm.filterVersions(ctx, versions)
171 if err != nil {
172 return nil, err
173 }
174
175 lookup := func(v string) (*modfetch.RevInfo, error) {
176 rev, err := repo.Stat(v)
177 if err != nil {
178 return nil, err
179 }
180
181 if (query == "upgrade" || query == "patch") && module.IsPseudoVersion(current) && !rev.Time.IsZero() {
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200 currentTime, err := module.PseudoVersionTime(current)
201 if err == nil && rev.Time.Before(currentTime) {
202 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
203 return nil, err
204 }
205 return repo.Stat(current)
206 }
207 }
208
209 return rev, nil
210 }
211
212 if qm.preferLower {
213 if len(releases) > 0 {
214 return lookup(releases[0])
215 }
216 if len(prereleases) > 0 {
217 return lookup(prereleases[0])
218 }
219 } else {
220 if len(releases) > 0 {
221 return lookup(releases[len(releases)-1])
222 }
223 if len(prereleases) > 0 {
224 return lookup(prereleases[len(prereleases)-1])
225 }
226 }
227
228 if qm.mayUseLatest {
229 latest, err := repo.Latest()
230 if err == nil {
231 if qm.allowsVersion(ctx, latest.Version) {
232 return lookup(latest.Version)
233 }
234 } else if !errors.Is(err, fs.ErrNotExist) {
235 return nil, err
236 }
237 }
238
239 if (query == "upgrade" || query == "patch") && current != "" && current != "none" {
240
241 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
242 return nil, err
243 }
244 return lookup(current)
245 }
246
247 return nil, &NoMatchingVersionError{query: query, current: current}
248 }
249
250
251
252
253
254 func IsRevisionQuery(vers string) bool {
255 if vers == "latest" ||
256 vers == "upgrade" ||
257 vers == "patch" ||
258 strings.HasPrefix(vers, "<") ||
259 strings.HasPrefix(vers, ">") ||
260 (semver.IsValid(vers) && isSemverPrefix(vers)) {
261 return false
262 }
263 return true
264 }
265
266
267
268 func isSemverPrefix(v string) bool {
269 dots := 0
270 for i := 0; i < len(v); i++ {
271 switch v[i] {
272 case '-', '+':
273 return false
274 case '.':
275 dots++
276 if dots >= 2 {
277 return false
278 }
279 }
280 }
281 return true
282 }
283
284 type queryMatcher struct {
285 path string
286 prefix string
287 filter func(version string) bool
288 allowed AllowedFunc
289 canStat bool
290 preferLower bool
291 mayUseLatest bool
292 preferIncompatible bool
293 }
294
295 var errRevQuery = errors.New("query refers to a non-semver revision")
296
297
298
299
300
301
302 func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (*queryMatcher, error) {
303 badVersion := func(v string) (*queryMatcher, error) {
304 return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query)
305 }
306
307 matchesMajor := func(v string) bool {
308 _, pathMajor, ok := module.SplitPathVersion(path)
309 if !ok {
310 return false
311 }
312 return module.CheckPathMajor(v, pathMajor) == nil
313 }
314
315 qm := &queryMatcher{
316 path: path,
317 allowed: allowed,
318 preferIncompatible: strings.HasSuffix(current, "+incompatible"),
319 }
320
321 switch {
322 case query == "latest":
323 qm.mayUseLatest = true
324
325 case query == "upgrade":
326 if current == "" || current == "none" {
327 qm.mayUseLatest = true
328 } else {
329 qm.mayUseLatest = module.IsPseudoVersion(current)
330 qm.filter = func(mv string) bool { return semver.Compare(mv, current) >= 0 }
331 }
332
333 case query == "patch":
334 if current == "" || current == "none" {
335 return nil, &NoPatchBaseError{path}
336 }
337 if current == "" {
338 qm.mayUseLatest = true
339 } else {
340 qm.mayUseLatest = module.IsPseudoVersion(current)
341 qm.prefix = semver.MajorMinor(current) + "."
342 qm.filter = func(mv string) bool { return semver.Compare(mv, current) >= 0 }
343 }
344
345 case strings.HasPrefix(query, "<="):
346 v := query[len("<="):]
347 if !semver.IsValid(v) {
348 return badVersion(v)
349 }
350 if isSemverPrefix(v) {
351
352 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
353 }
354 qm.filter = func(mv string) bool { return semver.Compare(mv, v) <= 0 }
355 if !matchesMajor(v) {
356 qm.preferIncompatible = true
357 }
358
359 case strings.HasPrefix(query, "<"):
360 v := query[len("<"):]
361 if !semver.IsValid(v) {
362 return badVersion(v)
363 }
364 qm.filter = func(mv string) bool { return semver.Compare(mv, v) < 0 }
365 if !matchesMajor(v) {
366 qm.preferIncompatible = true
367 }
368
369 case strings.HasPrefix(query, ">="):
370 v := query[len(">="):]
371 if !semver.IsValid(v) {
372 return badVersion(v)
373 }
374 qm.filter = func(mv string) bool { return semver.Compare(mv, v) >= 0 }
375 qm.preferLower = true
376 if !matchesMajor(v) {
377 qm.preferIncompatible = true
378 }
379
380 case strings.HasPrefix(query, ">"):
381 v := query[len(">"):]
382 if !semver.IsValid(v) {
383 return badVersion(v)
384 }
385 if isSemverPrefix(v) {
386
387 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
388 }
389 qm.filter = func(mv string) bool { return semver.Compare(mv, v) > 0 }
390 qm.preferLower = true
391 if !matchesMajor(v) {
392 qm.preferIncompatible = true
393 }
394
395 case semver.IsValid(query):
396 if isSemverPrefix(query) {
397 qm.prefix = query + "."
398
399
400 qm.filter = func(mv string) bool { return semver.Compare(mv, query) >= 0 }
401 } else {
402 qm.canStat = true
403 qm.filter = func(mv string) bool { return semver.Compare(mv, query) == 0 }
404 qm.prefix = semver.Canonical(query)
405 }
406 if !matchesMajor(query) {
407 qm.preferIncompatible = true
408 }
409
410 default:
411 return nil, errRevQuery
412 }
413
414 return qm, nil
415 }
416
417
418
419 func (qm *queryMatcher) allowsVersion(ctx context.Context, v string) bool {
420 if qm.prefix != "" && !strings.HasPrefix(v, qm.prefix) {
421 return false
422 }
423 if qm.filter != nil && !qm.filter(v) {
424 return false
425 }
426 if qm.allowed != nil {
427 if err := qm.allowed(ctx, module.Version{Path: qm.path, Version: v}); errors.Is(err, ErrDisallowed) {
428 return false
429 }
430 }
431 return true
432 }
433
434
435
436
437
438
439
440
441
442 func (qm *queryMatcher) filterVersions(ctx context.Context, versions []string) (releases, prereleases []string, err error) {
443 needIncompatible := qm.preferIncompatible
444
445 var lastCompatible string
446 for _, v := range versions {
447 if !qm.allowsVersion(ctx, v) {
448 continue
449 }
450
451 if !needIncompatible {
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468 if !strings.HasSuffix(v, "+incompatible") {
469 lastCompatible = v
470 } else if lastCompatible != "" {
471
472
473
474
475 ok, err := versionHasGoMod(ctx, module.Version{Path: qm.path, Version: lastCompatible})
476 if err != nil {
477 return nil, nil, err
478 }
479 if ok {
480
481
482
483
484 break
485 }
486
487
488
489
490 needIncompatible = true
491 }
492 }
493
494 if semver.Prerelease(v) != "" {
495 prereleases = append(prereleases, v)
496 } else {
497 releases = append(releases, v)
498 }
499 }
500
501 return releases, prereleases, nil
502 }
503
504 type QueryResult struct {
505 Mod module.Version
506 Rev *modfetch.RevInfo
507 Packages []string
508 }
509
510
511
512 func QueryPackages(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) {
513 pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)
514
515 if len(pkgMods) == 0 && err == nil {
516 replacement := Replacement(modOnly.Mod)
517 return nil, &PackageNotInModuleError{
518 Mod: modOnly.Mod,
519 Replacement: replacement,
520 Query: query,
521 Pattern: pattern,
522 }
523 }
524
525 return pkgMods, err
526 }
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543 func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) (pkgMods []QueryResult, modOnly *QueryResult, err error) {
544 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query)
545 defer span.Done()
546
547 base := pattern
548
549 firstError := func(m *search.Match) error {
550 if len(m.Errs) == 0 {
551 return nil
552 }
553 return m.Errs[0]
554 }
555
556 var match func(mod module.Version, roots []string, isLocal bool) *search.Match
557 matchPattern := search.MatchPattern(pattern)
558
559 if i := strings.Index(pattern, "..."); i >= 0 {
560 base = pathpkg.Dir(pattern[:i+3])
561 if base == "." {
562 return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query}
563 }
564 match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
565 m := search.NewMatch(pattern)
566 matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod})
567 return m
568 }
569 } else {
570 match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
571 m := search.NewMatch(pattern)
572 prefix := mod.Path
573 if MainModules.Contains(mod.Path) {
574 prefix = MainModules.PathPrefix(module.Version{Path: mod.Path})
575 }
576 for _, root := range roots {
577 if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
578 m.AddError(err)
579 } else if ok {
580 m.Pkgs = []string{pattern}
581 }
582 }
583 return m
584 }
585 }
586
587 var mainModuleMatches []module.Version
588 for _, mainModule := range MainModules.Versions() {
589 m := match(mainModule, modRoots, true)
590 if len(m.Pkgs) > 0 {
591 if query != "upgrade" && query != "patch" {
592 return nil, nil, &QueryMatchesPackagesInMainModuleError{
593 Pattern: pattern,
594 Query: query,
595 Packages: m.Pkgs,
596 }
597 }
598 if err := allowed(ctx, mainModule); err != nil {
599 return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, mainModule.Path, err)
600 }
601 return []QueryResult{{
602 Mod: mainModule,
603 Rev: &modfetch.RevInfo{Version: mainModule.Version},
604 Packages: m.Pkgs,
605 }}, nil, nil
606 }
607 if err := firstError(m); err != nil {
608 return nil, nil, err
609 }
610
611 var matchesMainModule bool
612 if matchPattern(mainModule.Path) {
613 mainModuleMatches = append(mainModuleMatches, mainModule)
614 matchesMainModule = true
615 }
616
617 if (query == "upgrade" || query == "patch") && matchesMainModule {
618 if err := allowed(ctx, mainModule); err == nil {
619 modOnly = &QueryResult{
620 Mod: mainModule,
621 Rev: &modfetch.RevInfo{Version: mainModule.Version},
622 }
623 }
624 }
625 }
626
627 var (
628 results []QueryResult
629 candidateModules = modulePrefixesExcludingTarget(base)
630 )
631 if len(candidateModules) == 0 {
632 if modOnly != nil {
633 return nil, modOnly, nil
634 } else if len(mainModuleMatches) != 0 {
635 return nil, nil, &QueryMatchesMainModulesError{
636 MainModules: mainModuleMatches,
637 Pattern: pattern,
638 Query: query,
639 }
640 } else {
641 return nil, nil, &PackageNotInModuleError{
642 MainModules: mainModuleMatches,
643 Query: query,
644 Pattern: pattern,
645 }
646 }
647 }
648
649 err = modfetch.TryProxies(func(proxy string) error {
650 queryModule := func(ctx context.Context, path string) (r QueryResult, err error) {
651 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path)
652 defer span.Done()
653
654 pathCurrent := current(path)
655 r.Mod.Path = path
656 r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed)
657 if err != nil {
658 return r, err
659 }
660 r.Mod.Version = r.Rev.Version
661 needSum := true
662 root, isLocal, err := fetch(ctx, r.Mod, needSum)
663 if err != nil {
664 return r, err
665 }
666 m := match(r.Mod, []string{root}, isLocal)
667 r.Packages = m.Pkgs
668 if len(r.Packages) == 0 && !matchPattern(path) {
669 if err := firstError(m); err != nil {
670 return r, err
671 }
672 replacement := Replacement(r.Mod)
673 return r, &PackageNotInModuleError{
674 Mod: r.Mod,
675 Replacement: replacement,
676 Query: query,
677 Pattern: pattern,
678 }
679 }
680 return r, nil
681 }
682
683 allResults, err := queryPrefixModules(ctx, candidateModules, queryModule)
684 results = allResults[:0]
685 for _, r := range allResults {
686 if len(r.Packages) == 0 {
687 modOnly = &r
688 } else {
689 results = append(results, r)
690 }
691 }
692 return err
693 })
694
695 if len(mainModuleMatches) > 0 && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
696 return nil, nil, &QueryMatchesMainModulesError{
697 Pattern: pattern,
698 Query: query,
699 }
700 }
701 return results[:len(results):len(results)], modOnly, err
702 }
703
704
705
706
707
708
709 func modulePrefixesExcludingTarget(path string) []string {
710 prefixes := make([]string, 0, strings.Count(path, "/")+1)
711
712 mainModulePrefixes := make(map[string]bool)
713 for _, m := range MainModules.Versions() {
714 mainModulePrefixes[m.Path] = true
715 }
716
717 for {
718 if !mainModulePrefixes[path] {
719 if _, _, ok := module.SplitPathVersion(path); ok {
720 prefixes = append(prefixes, path)
721 }
722 }
723
724 j := strings.LastIndexByte(path, '/')
725 if j < 0 {
726 break
727 }
728 path = path[:j]
729 }
730
731 return prefixes
732 }
733
734 func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) {
735 ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules")
736 defer span.Done()
737
738
739
740
741
742 type result struct {
743 QueryResult
744 err error
745 }
746 results := make([]result, len(candidateModules))
747 var wg sync.WaitGroup
748 wg.Add(len(candidateModules))
749 for i, p := range candidateModules {
750 ctx := trace.StartGoroutine(ctx)
751 go func(p string, r *result) {
752 r.QueryResult, r.err = queryModule(ctx, p)
753 wg.Done()
754 }(p, &results[i])
755 }
756 wg.Wait()
757
758
759
760
761 var (
762 noPackage *PackageNotInModuleError
763 noVersion *NoMatchingVersionError
764 noPatchBase *NoPatchBaseError
765 invalidPath *module.InvalidPathError
766 notExistErr error
767 )
768 for _, r := range results {
769 switch rErr := r.err.(type) {
770 case nil:
771 found = append(found, r.QueryResult)
772 case *PackageNotInModuleError:
773
774
775 if noPackage == nil || MainModules.Contains(noPackage.Mod.Path) {
776 noPackage = rErr
777 }
778 case *NoMatchingVersionError:
779 if noVersion == nil {
780 noVersion = rErr
781 }
782 case *NoPatchBaseError:
783 if noPatchBase == nil {
784 noPatchBase = rErr
785 }
786 case *module.InvalidPathError:
787
788
789
790
791
792
793
794 if invalidPath == nil {
795 invalidPath = rErr
796 }
797 default:
798 if errors.Is(rErr, fs.ErrNotExist) {
799 if notExistErr == nil {
800 notExistErr = rErr
801 }
802 } else if err == nil {
803 if len(found) > 0 || noPackage != nil {
804
805
806
807
808
809
810
811 } else {
812 err = r.err
813 }
814 }
815 }
816 }
817
818
819
820
821
822 if len(found) == 0 && err == nil {
823 switch {
824 case noPackage != nil:
825 err = noPackage
826 case noVersion != nil:
827 err = noVersion
828 case noPatchBase != nil:
829 err = noPatchBase
830 case invalidPath != nil:
831 err = invalidPath
832 case notExistErr != nil:
833 err = notExistErr
834 default:
835 panic("queryPrefixModules: no modules found, but no error detected")
836 }
837 }
838
839 return found, err
840 }
841
842
843
844
845
846
847
848
849
850 type NoMatchingVersionError struct {
851 query, current string
852 }
853
854 func (e *NoMatchingVersionError) Error() string {
855 currentSuffix := ""
856 if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" {
857 currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
858 }
859 return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
860 }
861
862
863
864 type NoPatchBaseError struct {
865 path string
866 }
867
868 func (e *NoPatchBaseError) Error() string {
869 return fmt.Sprintf(`can't query version "patch" of module %s: no existing version is required`, e.path)
870 }
871
872
873
874
875 type WildcardInFirstElementError struct {
876 Pattern string
877 Query string
878 }
879
880 func (e *WildcardInFirstElementError) Error() string {
881 return fmt.Sprintf("no modules to query for %s@%s because first path element contains a wildcard", e.Pattern, e.Query)
882 }
883
884
885
886
887
888
889
890
891
892
893 type PackageNotInModuleError struct {
894 MainModules []module.Version
895 Mod module.Version
896 Replacement module.Version
897 Query string
898 Pattern string
899 }
900
901 func (e *PackageNotInModuleError) Error() string {
902 if len(e.MainModules) > 0 {
903 prefix := "workspace modules do"
904 if len(e.MainModules) == 1 {
905 prefix = fmt.Sprintf("main module (%s) does", e.MainModules[0])
906 }
907 if strings.Contains(e.Pattern, "...") {
908 return fmt.Sprintf("%s not contain packages matching %s", prefix, e.Pattern)
909 }
910 return fmt.Sprintf("%s not contain package %s", prefix, e.Pattern)
911 }
912
913 found := ""
914 if r := e.Replacement; r.Path != "" {
915 replacement := r.Path
916 if r.Version != "" {
917 replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
918 }
919 if e.Query == e.Mod.Version {
920 found = fmt.Sprintf(" (replaced by %s)", replacement)
921 } else {
922 found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
923 }
924 } else if e.Query != e.Mod.Version {
925 found = fmt.Sprintf(" (%s)", e.Mod.Version)
926 }
927
928 if strings.Contains(e.Pattern, "...") {
929 return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
930 }
931 return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
932 }
933
934 func (e *PackageNotInModuleError) ImportPath() string {
935 if !strings.Contains(e.Pattern, "...") {
936 return e.Pattern
937 }
938 return ""
939 }
940
941
942 func moduleHasRootPackage(ctx context.Context, m module.Version) (bool, error) {
943 needSum := false
944 root, isLocal, err := fetch(ctx, m, needSum)
945 if err != nil {
946 return false, err
947 }
948 _, ok, err := dirInModule(m.Path, m.Path, root, isLocal)
949 return ok, err
950 }
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971 func versionHasGoMod(_ context.Context, m module.Version) (bool, error) {
972 _, data, err := rawGoModData(m)
973 if err != nil {
974 return false, err
975 }
976 isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path))
977 return !isFake, nil
978 }
979
980
981
982 type versionRepo interface {
983 ModulePath() string
984 Versions(prefix string) ([]string, error)
985 Stat(rev string) (*modfetch.RevInfo, error)
986 Latest() (*modfetch.RevInfo, error)
987 }
988
989 var _ versionRepo = modfetch.Repo(nil)
990
991 func lookupRepo(proxy, path string) (repo versionRepo, err error) {
992 err = module.CheckPath(path)
993 if err == nil {
994 repo = modfetch.Lookup(proxy, path)
995 } else {
996 repo = emptyRepo{path: path, err: err}
997 }
998
999 if MainModules == nil {
1000 return repo, err
1001 } else if _, ok := MainModules.HighestReplaced()[path]; ok {
1002 return &replacementRepo{repo: repo}, nil
1003 }
1004
1005 return repo, err
1006 }
1007
1008
1009 type emptyRepo struct {
1010 path string
1011 err error
1012 }
1013
1014 var _ versionRepo = emptyRepo{}
1015
1016 func (er emptyRepo) ModulePath() string { return er.path }
1017 func (er emptyRepo) Versions(prefix string) ([]string, error) { return nil, nil }
1018 func (er emptyRepo) Stat(rev string) (*modfetch.RevInfo, error) { return nil, er.err }
1019 func (er emptyRepo) Latest() (*modfetch.RevInfo, error) { return nil, er.err }
1020
1021
1022
1023
1024
1025
1026
1027 type replacementRepo struct {
1028 repo versionRepo
1029 }
1030
1031 var _ versionRepo = (*replacementRepo)(nil)
1032
1033 func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() }
1034
1035
1036
1037 func (rr *replacementRepo) Versions(prefix string) ([]string, error) {
1038 repoVersions, err := rr.repo.Versions(prefix)
1039 if err != nil && !errors.Is(err, os.ErrNotExist) {
1040 return nil, err
1041 }
1042
1043 versions := repoVersions
1044 for _, mm := range MainModules.Versions() {
1045 if index := MainModules.Index(mm); index != nil && len(index.replace) > 0 {
1046 path := rr.ModulePath()
1047 for m, _ := range index.replace {
1048 if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) {
1049 versions = append(versions, m.Version)
1050 }
1051 }
1052 }
1053 }
1054
1055 if len(versions) == len(repoVersions) {
1056 return versions, nil
1057 }
1058
1059 sort.Slice(versions, func(i, j int) bool {
1060 return semver.Compare(versions[i], versions[j]) < 0
1061 })
1062 str.Uniq(&versions)
1063 return versions, nil
1064 }
1065
1066 func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) {
1067 info, err := rr.repo.Stat(rev)
1068 if err == nil {
1069 return info, err
1070 }
1071 var hasReplacements bool
1072 for _, v := range MainModules.Versions() {
1073 if index := MainModules.Index(v); index != nil && len(index.replace) > 0 {
1074 hasReplacements = true
1075 }
1076 }
1077 if !hasReplacements {
1078 return info, err
1079 }
1080
1081 v := module.CanonicalVersion(rev)
1082 if v != rev {
1083
1084
1085 return info, err
1086 }
1087
1088 path := rr.ModulePath()
1089 _, pathMajor, ok := module.SplitPathVersion(path)
1090 if ok && pathMajor == "" {
1091 if err := module.CheckPathMajor(v, pathMajor); err != nil && semver.Build(v) == "" {
1092 v += "+incompatible"
1093 }
1094 }
1095
1096 if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
1097 return info, err
1098 }
1099 return rr.replacementStat(v)
1100 }
1101
1102 func (rr *replacementRepo) Latest() (*modfetch.RevInfo, error) {
1103 info, err := rr.repo.Latest()
1104 path := rr.ModulePath()
1105
1106 if v, ok := MainModules.HighestReplaced()[path]; ok {
1107 if v == "" {
1108
1109
1110
1111
1112
1113 if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 {
1114 v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
1115 } else {
1116 v = module.PseudoVersion("v0", "", time.Time{}, "000000000000")
1117 }
1118 }
1119
1120 if err != nil || semver.Compare(v, info.Version) > 0 {
1121 return rr.replacementStat(v)
1122 }
1123 }
1124
1125 return info, err
1126 }
1127
1128 func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) {
1129 rev := &modfetch.RevInfo{Version: v}
1130 if module.IsPseudoVersion(v) {
1131 rev.Time, _ = module.PseudoVersionTime(v)
1132 rev.Short, _ = module.PseudoVersionRev(v)
1133 }
1134 return rev, nil
1135 }
1136
1137
1138
1139
1140 type QueryMatchesMainModulesError struct {
1141 MainModules []module.Version
1142 Pattern string
1143 Query string
1144 }
1145
1146 func (e *QueryMatchesMainModulesError) Error() string {
1147 if MainModules.Contains(e.Pattern) {
1148 return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern)
1149 }
1150
1151 plural := ""
1152 mainModulePaths := make([]string, len(e.MainModules))
1153 for i := range e.MainModules {
1154 mainModulePaths[i] = e.MainModules[i].Path
1155 }
1156 if len(e.MainModules) > 1 {
1157 plural = "s"
1158 }
1159 return fmt.Sprintf("can't request version %q of pattern %q that includes the main module%s (%s)", e.Query, e.Pattern, plural, strings.Join(mainModulePaths, ", "))
1160 }
1161
1162
1163
1164
1165 type QueryUpgradesAllError struct {
1166 MainModules []module.Version
1167 Query string
1168 }
1169
1170 func (e *QueryUpgradesAllError) Error() string {
1171 var plural string = ""
1172 if len(e.MainModules) != 1 {
1173 plural = "s"
1174 }
1175
1176 return fmt.Sprintf("can't request version %q of pattern \"all\" that includes the main module%s", e.Query, plural)
1177 }
1178
1179
1180
1181 type QueryMatchesPackagesInMainModuleError struct {
1182 Pattern string
1183 Query string
1184 Packages []string
1185 }
1186
1187 func (e *QueryMatchesPackagesInMainModuleError) Error() string {
1188 if len(e.Packages) > 1 {
1189 return fmt.Sprintf("pattern %s matches %d packages in the main module, so can't request version %s", e.Pattern, len(e.Packages), e.Query)
1190 }
1191
1192 if search.IsMetaPackage(e.Pattern) || strings.Contains(e.Pattern, "...") {
1193 return fmt.Sprintf("pattern %s matches package %s in the main module, so can't request version %s", e.Pattern, e.Packages[0], e.Query)
1194 }
1195
1196 return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query)
1197 }
1198
View as plain text