Skip to content

Commit 04b5b4f

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/modload: return a module-only result from QueryPattern
This allows a single QueryPattern call to resolve a path that could be either a package or a module. It is important to be able to make a single QueryPattern call — rather than a QueryPattern followed by a Query for the specific module path — to provide appropriate fallback behavior: if the proxy returns package results but does not contain a module result, we don't want to fall back to the next proxy to look for the (probably-nonexistent) module. For #37438 Change-Id: I419b8bb3ab4565f443bb5cee9a8b206f453b9801 Reviewed-on: https://go-review.googlesource.com/c/go/+/266657 Trust: Bryan C. Mills <[email protected]> Run-TryBot: Bryan C. Mills <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent 40f0359 commit 04b5b4f

File tree

5 files changed

+81
-44
lines changed

5 files changed

+81
-44
lines changed

src/cmd/go/internal/modget/get.go

+6-13
Original file line numberDiff line numberDiff line change
@@ -912,22 +912,15 @@ func getQuery(ctx context.Context, path, vers string, prevM module.Version, forc
912912
// If it turns out to only exist as a module, we can detect the resulting
913913
// PackageNotInModuleError and avoid a second round-trip through (potentially)
914914
// all of the configured proxies.
915-
results, err := modload.QueryPattern(ctx, path, vers, modload.Selected, allowed)
915+
results, modOnly, err := modload.QueryPattern(ctx, path, vers, modload.Selected, allowed)
916916
if err != nil {
917-
// If the path doesn't contain a wildcard, check whether it was actually a
918-
// module path instead. If so, return that.
919-
if !strings.Contains(path, "...") {
920-
var modErr *modload.PackageNotInModuleError
921-
if errors.As(err, &modErr) && modErr.Mod.Path == path {
922-
if modErr.Mod.Version != vers {
923-
logOncef("go: %s %s => %s", path, vers, modErr.Mod.Version)
924-
}
925-
return modErr.Mod, nil
926-
}
927-
}
928-
929917
return module.Version{}, err
930918
}
919+
if len(results) == 0 {
920+
// The path doesn't contain a wildcard, but was actually a
921+
// module path instead. Return that.
922+
return modOnly.Mod, nil
923+
}
931924

932925
m := results[0].Mod
933926
if m.Path != path {

src/cmd/go/internal/modload/import.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ func queryImport(ctx context.Context, path string) (module.Version, error) {
383383
// and return m, dir, ImpportMissingError.
384384
fmt.Fprintf(os.Stderr, "go: finding module for package %s\n", path)
385385

386-
candidates, err := QueryPattern(ctx, path, "latest", Selected, CheckAllowed)
386+
candidates, err := QueryPackages(ctx, path, "latest", Selected, CheckAllowed)
387387
if err != nil {
388388
if errors.Is(err, fs.ErrNotExist) {
389389
// Return "cannot find module providing package […]" instead of whatever

src/cmd/go/internal/modload/query.go

+60-26
Original file line numberDiff line numberDiff line change
@@ -504,20 +504,39 @@ type QueryResult struct {
504504
Packages []string
505505
}
506506

507+
// QueryPackages is like QueryPattern, but requires that the pattern match at
508+
// least one package and omits the non-package result (if any).
509+
func QueryPackages(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) {
510+
pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)
511+
512+
if len(pkgMods) == 0 && err == nil {
513+
return nil, &PackageNotInModuleError{
514+
Mod: modOnly.Mod,
515+
Replacement: Replacement(modOnly.Mod),
516+
Query: query,
517+
Pattern: pattern,
518+
}
519+
}
520+
521+
return pkgMods, err
522+
}
523+
507524
// QueryPattern looks up the module(s) containing at least one package matching
508525
// the given pattern at the given version. The results are sorted by module path
509-
// length in descending order.
526+
// length in descending order. If any proxy provides a non-empty set of candidate
527+
// modules, no further proxies are tried.
510528
//
511-
// QueryPattern queries modules with package paths up to the first "..."
512-
// in the pattern. For the pattern "example.com/a/b.../c", QueryPattern would
513-
// consider prefixes of "example.com/a". If multiple modules have versions
514-
// that match the query and packages that match the pattern, QueryPattern
515-
// picks the one with the longest module path.
529+
// For wildcard patterns, QueryPattern looks in modules with package paths up to
530+
// the first "..." in the pattern. For the pattern "example.com/a/b.../c",
531+
// QueryPattern would consider prefixes of "example.com/a".
516532
//
517533
// If any matching package is in the main module, QueryPattern considers only
518534
// the main module and only the version "latest", without checking for other
519535
// possible modules.
520-
func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) {
536+
//
537+
// QueryPattern always returns at least one QueryResult (which may be only
538+
// modOnly) or a non-nil error.
539+
func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) (pkgMods []QueryResult, modOnly *QueryResult, err error) {
521540
ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query)
522541
defer span.Done()
523542

@@ -531,6 +550,7 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
531550
}
532551

533552
var match func(mod module.Version, root string, isLocal bool) *search.Match
553+
matchPattern := search.MatchPattern(pattern)
534554

535555
if i := strings.Index(pattern, "..."); i >= 0 {
536556
base = pathpkg.Dir(pattern[:i+3])
@@ -558,20 +578,29 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
558578
if HasModRoot() {
559579
m := match(Target, modRoot, true)
560580
if len(m.Pkgs) > 0 {
561-
if query != "latest" {
562-
return nil, fmt.Errorf("can't query specific version for package %s in the main module (%s)", pattern, Target.Path)
581+
if query != "latest" && query != "upgrade" && query != "patch" {
582+
return nil, nil, fmt.Errorf("can't query version %q for package %s in the main module (%s)", query, pattern, Target.Path)
563583
}
564584
if err := allowed(ctx, Target); err != nil {
565-
return nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, Target.Path, err)
585+
return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, Target.Path, err)
566586
}
567587
return []QueryResult{{
568588
Mod: Target,
569589
Rev: &modfetch.RevInfo{Version: Target.Version},
570590
Packages: m.Pkgs,
571-
}}, nil
591+
}}, nil, nil
572592
}
573593
if err := firstError(m); err != nil {
574-
return nil, err
594+
return nil, nil, err
595+
}
596+
597+
if query != "latest" && query != "upgrade" && query != "patch" && matchPattern(Target.Path) {
598+
if err := allowed(ctx, Target); err == nil {
599+
modOnly = &QueryResult{
600+
Mod: Target,
601+
Rev: &modfetch.RevInfo{Version: Target.Version},
602+
}
603+
}
575604
}
576605
}
577606

@@ -580,14 +609,17 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
580609
candidateModules = modulePrefixesExcludingTarget(base)
581610
)
582611
if len(candidateModules) == 0 {
583-
return nil, &PackageNotInModuleError{
584-
Mod: Target,
585-
Query: query,
586-
Pattern: pattern,
612+
if modOnly == nil {
613+
return nil, nil, &PackageNotInModuleError{
614+
Mod: Target,
615+
Query: query,
616+
Pattern: pattern,
617+
}
587618
}
619+
return nil, modOnly, nil
588620
}
589621

590-
err := modfetch.TryProxies(func(proxy string) error {
622+
err = modfetch.TryProxies(func(proxy string) error {
591623
queryModule := func(ctx context.Context, path string) (r QueryResult, err error) {
592624
ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path)
593625
defer span.Done()
@@ -606,7 +638,7 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
606638
}
607639
m := match(r.Mod, root, isLocal)
608640
r.Packages = m.Pkgs
609-
if len(r.Packages) == 0 {
641+
if len(r.Packages) == 0 && !matchPattern(path) {
610642
if err := firstError(m); err != nil {
611643
return r, err
612644
}
@@ -620,12 +652,19 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
620652
return r, nil
621653
}
622654

623-
var err error
624-
results, err = queryPrefixModules(ctx, candidateModules, queryModule)
655+
allResults, err := queryPrefixModules(ctx, candidateModules, queryModule)
656+
results = allResults[:0]
657+
for _, r := range allResults {
658+
if len(r.Packages) == 0 {
659+
modOnly = &r
660+
} else {
661+
results = append(results, r)
662+
}
663+
}
625664
return err
626665
})
627666

628-
return results, err
667+
return results[:len(results):len(results)], modOnly, err
629668
}
630669

631670
// modulePrefixesExcludingTarget returns all prefixes of path that may plausibly
@@ -651,11 +690,6 @@ func modulePrefixesExcludingTarget(path string) []string {
651690
return prefixes
652691
}
653692

654-
type prefixResult struct {
655-
QueryResult
656-
err error
657-
}
658-
659693
func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) {
660694
ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules")
661695
defer span.Done()

src/cmd/go/internal/work/build.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ func installOutsideModule(ctx context.Context, args []string) {
741741
// Don't check for retractions if a specific revision is requested.
742742
allowed = nil
743743
}
744-
qrs, err := modload.QueryPattern(ctx, patterns[0], version, modload.Selected, allowed)
744+
qrs, err := modload.QueryPackages(ctx, patterns[0], version, modload.Selected, allowed)
745745
if err != nil {
746746
base.Fatalf("go install %s: %v", args[0], err)
747747
}

src/cmd/go/testdata/script/mod_get_downgrade_missing.txt

+13-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,24 @@ cp go.mod go.mod.orig
33
# getting a specific version of a module along with a pattern
44
# not yet present in that module should report the version mismatch
55
# rather than a "matched no packages" warning.
6+
67
! go get example.net/[email protected] example.net/pkgadded/subpkg/...
78
stderr '^go get: conflicting versions for module example\.net/pkgadded: v1\.1\.0 and v1\.2\.0$'
89
! stderr 'matched no packages'
910
cmp go.mod.orig go.mod
1011

11-
! go get example.net/pkgadded/[email protected]
12-
stderr '^go get example\.net/pkgadded/\.\.\.@v1\.0\.0: module example\.net/pkgadded@v1\.0\.0 found, but does not contain packages matching example\.net/pkgadded/\.\.\.$'
13-
cmp go.mod.orig go.mod
12+
13+
# A wildcard pattern should match a package in a module with that path.
14+
15+
go get example.net/pkgadded/[email protected]
16+
go list -m all
17+
stdout '^example.net/pkgadded v1.0.0'
18+
cp go.mod.orig go.mod
19+
20+
21+
# If we need to resolve a transitive dependency of a package,
22+
# and another argument constrains away the version that provides that
23+
# package, then 'go get' should fail with a useful error message.
1424

1525
! go get example.net/[email protected] .
1626
stderr -count=1 '^go: found example.net/pkgadded/subpkg in example.net/pkgadded v1\.2\.0$' # TODO: We shouldn't even try v1.2.0.

0 commit comments

Comments
 (0)