Skip to content

Commit 261609f

Browse files
author
Bryan C. Mills
committed
cmd/go/internal: factor out modload.QueryPackage and use in in modget
modload.Import contains a loop that looks for the module containing a package. Because we overload Import to locate both packages and modules, that loop contains a bunch of special-cases for modules with empty roots. In this change, we factor out the loop into a new function (QueryPackage) and use that directly in modget.getQuery. That restores the invariant that the paths passed to modload.Import must be importable packages, and fixes 'go get' lookups for packages that have moved between a module and submodules with the same path prefix. Updates #26602. Change-Id: I8bc8340c17f2df062d03ce720f4dc18b2ba406b2 Reviewed-on: https://go-review.googlesource.com/128136 Reviewed-by: Russ Cox <[email protected]>
1 parent a1cbbe0 commit 261609f

14 files changed

+172
-98
lines changed

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

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ func runGet(cmd *base.Command, args []string) {
528528
// current directory, even if it is not tho module root.
529529
continue
530530
}
531-
if strings.HasPrefix(p.Error.Err, "no Go files") && modload.ModuleInfo(p.ImportPath) != nil {
531+
if strings.Contains(p.Error.Err, "cannot find module providing") && modload.ModuleInfo(p.ImportPath) != nil {
532532
// Explicitly-requested module, but it doesn't contain a package at the
533533
// module root.
534534
continue
@@ -551,13 +551,6 @@ func runGet(cmd *base.Command, args []string) {
551551
// If forceModulePath is set, getQuery must interpret path
552552
// as a module path.
553553
func getQuery(path, vers string, forceModulePath bool) (module.Version, error) {
554-
if path == modload.Target.Path {
555-
if vers != "" {
556-
return module.Version{}, fmt.Errorf("cannot update main module to explicit version")
557-
}
558-
return modload.Target, nil
559-
}
560-
561554
if vers == "" {
562555
vers = "latest"
563556
}
@@ -569,36 +562,14 @@ func getQuery(path, vers string, forceModulePath bool) (module.Version, error) {
569562
return module.Version{Path: path, Version: info.Version}, nil
570563
}
571564

572-
// Even if the query fails, if the path is (or must be) a real module, then report the query error.
573-
if forceModulePath || *getM || isModulePath(path) {
574-
return module.Version{}, err
575-
}
576-
577-
// Otherwise, interpret the package path as an import
578-
// and determine what module that import would address
579-
// if found in the current source code.
580-
// Then apply the version to that module.
581-
m, _, err := modload.Import(path)
582-
if e, ok := err.(*modload.ImportMissingError); ok && e.Module.Path != "" {
583-
m = e.Module
584-
} else if err != nil {
565+
// Even if the query fails, if the path must be a real module, then report the query error.
566+
if forceModulePath || *getM {
585567
return module.Version{}, err
586568
}
587-
if m.Path == "" {
588-
return module.Version{}, fmt.Errorf("package %q is not in a module", path)
589-
}
590-
info, err = modload.Query(m.Path, vers, modload.Allowed)
591-
if err != nil {
592-
return module.Version{}, err
593-
}
594-
return module.Version{Path: m.Path, Version: info.Version}, nil
595-
}
596569

597-
// isModulePath reports whether path names an actual module,
598-
// defined as one with an accessible latest version.
599-
func isModulePath(path string) bool {
600-
_, err := modload.Query(path, "latest", modload.Allowed)
601-
return err == nil
570+
// Otherwise, try a package path.
571+
m, _, err := modload.QueryPackage(path, vers, modload.Allowed)
572+
return m, err
602573
}
603574

604575
// An upgrader adapts an underlying mvs.Reqs to apply an

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

Lines changed: 4 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"fmt"
1111
"go/build"
1212
"os"
13-
pathpkg "path"
1413
"path/filepath"
1514
"strings"
1615

@@ -124,24 +123,6 @@ func Import(path string) (m module.Version, dir string, err error) {
124123
return module.Version{}, "", errors.New(buf.String())
125124
}
126125

127-
// Special case: if the path matches a module path,
128-
// and we haven't found code in any module on the build list
129-
// (since we haven't returned yet),
130-
// force the use of the current module instead of
131-
// looking for an alternate one.
132-
// This helps "go get golang.org/x/net" even though
133-
// there is no code in x/net.
134-
for _, m := range buildList {
135-
if m.Path == path {
136-
root, isLocal, err := fetch(m)
137-
if err != nil {
138-
return module.Version{}, "", err
139-
}
140-
dir, _ := dirInModule(path, m.Path, root, isLocal)
141-
return m, dir, nil
142-
}
143-
}
144-
145126
// Not on build list.
146127

147128
// Look up module containing the package, for addition to the build list.
@@ -150,43 +131,11 @@ func Import(path string) (m module.Version, dir string, err error) {
150131
return module.Version{}, "", fmt.Errorf("import lookup disabled by -mod=%s", cfg.BuildMod)
151132
}
152133

153-
for p := path; p != "."; p = pathpkg.Dir(p) {
154-
// We can't upgrade the main module.
155-
// Note that this loop does consider upgrading other modules on the build list.
156-
// If that's too aggressive we can skip all paths already on the build list,
157-
// not just Target.Path, but for now let's try being aggressive.
158-
if p == Target.Path {
159-
// Can't move to a new version of main module.
160-
continue
161-
}
162-
163-
info, err := Query(p, "latest", Allowed)
164-
if err != nil {
165-
continue
166-
}
167-
m := module.Version{Path: p, Version: info.Version}
168-
root, isLocal, err := fetch(m)
169-
if err != nil {
170-
continue
171-
}
172-
_, ok := dirInModule(path, m.Path, root, isLocal)
173-
if ok {
174-
return module.Version{}, "", &ImportMissingError{ImportPath: path, Module: m}
175-
}
176-
177-
// Special case matching the one above:
178-
// if m.Path matches path, assume adding it to the build list
179-
// will either add the right code or the right code doesn't exist.
180-
if m.Path == path {
181-
return module.Version{}, "", &ImportMissingError{ImportPath: path, Module: m}
182-
}
134+
m, _, err = QueryPackage(path, "latest", Allowed)
135+
if err != nil {
136+
return module.Version{}, "", &ImportMissingError{ImportPath: path}
183137
}
184-
185-
// Did not resolve import to any module.
186-
// TODO(rsc): It would be nice to return a specific error encountered
187-
// during the loop above if possible, but it's not clear how to pick
188-
// out the right one.
189-
return module.Version{}, "", &ImportMissingError{ImportPath: path}
138+
return m, "", &ImportMissingError{ImportPath: path, Module: m}
190139
}
191140

192141
// maybeInModule reports whether, syntactically,

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"cmd/go/internal/module"
1010
"cmd/go/internal/semver"
1111
"fmt"
12+
pathpkg "path"
1213
"strings"
1314
)
1415

@@ -29,6 +30,8 @@ import (
2930
//
3031
// If the allowed function is non-nil, Query excludes any versions for which allowed returns false.
3132
//
33+
// If path is the path of the main module and the query is "latest",
34+
// Query returns Target.Version as the version.
3235
func Query(path, query string, allowed func(module.Version) bool) (*modfetch.RevInfo, error) {
3336
if allowed == nil {
3437
allowed = func(module.Version) bool { return true }
@@ -117,6 +120,16 @@ func Query(path, query string, allowed func(module.Version) bool) (*modfetch.Rev
117120
return info, nil
118121
}
119122

123+
if path == Target.Path {
124+
if query != "latest" {
125+
return nil, fmt.Errorf("can't query specific version (%q) for the main module (%s)", query, path)
126+
}
127+
if !allowed(Target) {
128+
return nil, fmt.Errorf("internal error: main module version is not allowed")
129+
}
130+
return &modfetch.RevInfo{Version: Target.Version}, nil
131+
}
132+
120133
// Load versions and execute query.
121134
repo, err := modfetch.Lookup(path)
122135
if err != nil {
@@ -187,3 +200,44 @@ func isSemverPrefix(v string) bool {
187200
func matchSemverPrefix(p, v string) bool {
188201
return len(v) > len(p) && v[len(p)] == '.' && v[:len(p)] == p
189202
}
203+
204+
// QueryPackage looks up a revision of a module containing path.
205+
//
206+
// If multiple modules with revisions matching the query provide the requested
207+
// package, QueryPackage picks the one with the longest module path.
208+
//
209+
// If the path is in the the main module and the query is "latest",
210+
// QueryPackage returns Target as the version.
211+
func QueryPackage(path, query string, allowed func(module.Version) bool) (module.Version, *modfetch.RevInfo, error) {
212+
if _, ok := dirInModule(path, Target.Path, ModRoot, true); ok {
213+
if query != "latest" {
214+
return module.Version{}, nil, fmt.Errorf("can't query specific version (%q) for package %s in the main module (%s)", query, path, Target.Path)
215+
}
216+
if !allowed(Target) {
217+
return module.Version{}, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed", path, Target.Path)
218+
}
219+
return Target, &modfetch.RevInfo{Version: Target.Version}, nil
220+
}
221+
222+
finalErr := errMissing
223+
for p := path; p != "."; p = pathpkg.Dir(p) {
224+
info, err := Query(p, query, allowed)
225+
if err != nil {
226+
if finalErr == errMissing {
227+
finalErr = err
228+
}
229+
continue
230+
}
231+
m := module.Version{Path: p, Version: info.Version}
232+
root, isLocal, err := fetch(m)
233+
if err != nil {
234+
return module.Version{}, nil, err
235+
}
236+
_, ok := dirInModule(path, m.Path, root, isLocal)
237+
if ok {
238+
return m, info, nil
239+
}
240+
}
241+
242+
return module.Version{}, nil, finalErr
243+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Written by hand.
2+
Test case for package moved into a parent module.
3+
4+
-- .mod --
5+
module example.com/join/subpkg
6+
-- .info --
7+
{"Version": "v1.0.0"}
8+
-- x.go --
9+
package subpkg
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Written by hand.
2+
Test case for package moved into a parent module.
3+
4+
-- .mod --
5+
module example.com/join/subpkg
6+
7+
require example.com/join v1.1.0
8+
-- .info --
9+
{"Version": "v1.1.0"}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Written by hand.
2+
Test case for package moved into a parent module.
3+
4+
-- .mod --
5+
module example.com/join
6+
-- .info --
7+
{"Version": "v1.0.0"}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Written by hand.
2+
Test case for package moved into a parent module.
3+
4+
-- .mod --
5+
module example.com/join
6+
-- .info --
7+
{"Version": "v1.1.0"}
8+
-- subpkg/x.go --
9+
package subpkg
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Written by hand.
2+
Test case for getting a package that has been moved to a different module.
3+
4+
-- .mod --
5+
module example.com/split/subpkg
6+
7+
require example.com/split v1.1.0
8+
-- .info --
9+
{"Version": "v1.1.0"}
10+
-- x.go --
11+
package subpkg
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Written by hand.
2+
Test case for getting a package that has been moved to a different module.
3+
4+
-- .mod --
5+
module example.com/split
6+
-- .info --
7+
{"Version": "v1.0.0"}
8+
-- subpkg/x.go --
9+
package subpkg
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Written by hand.
2+
Test case for getting a package that has been moved to a different module.
3+
4+
-- .mod --
5+
module example.com/split
6+
7+
require example.com/split/subpkg v1.1.0
8+
-- .info --
9+
{"Version": "v1.1.0"}

0 commit comments

Comments
 (0)