Skip to content

Commit a1cbbe0

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/modload: report errors explicitly from Lookup
Previously, we reported errors directly in (*loader).load via base.Errorf. Unfortunately, (*loader).load can be called from contexts in which such errors should not be considered fatal, such as by load.PackagesAndErrors. Instead, we save the errors in pkg.err and modify Lookup to return that error. This change is a bit awkward: we end up suppressing a "no Go files" error for packages at the root of newly-imported modules, even if they really do contain source files. I believe that that's due to a special-case lookup for modules in the build list, which allows us to "validate" imports for modules in the build list even though we haven't actually downloaded their sources (or verified that they actually contain the requested package). The fix for that issue is in the change that follows this one. Fixes #26602. Change-Id: I16f00ceb143fbb797cfc3cb07fd08aeb6154575b Reviewed-on: https://go-review.googlesource.com/127936 Run-TryBot: Bryan C. Mills <[email protected]> Reviewed-by: Russ Cox <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 79bf795 commit a1cbbe0

File tree

8 files changed

+104
-50
lines changed

8 files changed

+104
-50
lines changed

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

+20-3
Original file line numberDiff line numberDiff line change
@@ -515,13 +515,28 @@ func runGet(cmd *base.Command, args []string) {
515515
}
516516

517517
if len(install) > 0 {
518+
// All requested versions were explicitly @none.
519+
// Note that 'go get -u' without any arguments results in len(install) == 1:
520+
// search.CleanImportPaths returns "." for empty args.
518521
work.BuildInit()
519522
var pkgs []string
520523
for _, p := range load.PackagesAndErrors(install) {
521-
if p.Error == nil || !strings.HasPrefix(p.Error.Err, "no Go files") {
522-
pkgs = append(pkgs, p.ImportPath)
524+
// Ignore "no Go source files" errors for 'go get' operations on modules.
525+
if p.Error != nil {
526+
if len(args) == 0 && getU != "" && strings.HasPrefix(p.Error.Err, "no Go files") {
527+
// Upgrading modules: skip the implicitly-requested package at the
528+
// current directory, even if it is not tho module root.
529+
continue
530+
}
531+
if strings.HasPrefix(p.Error.Err, "no Go files") && modload.ModuleInfo(p.ImportPath) != nil {
532+
// Explicitly-requested module, but it doesn't contain a package at the
533+
// module root.
534+
continue
535+
}
523536
}
537+
pkgs = append(pkgs, p.ImportPath)
524538
}
539+
525540
// If -d was specified, we're done after the download: no build.
526541
// (The load.PackagesAndErrors is what did the download
527542
// of the named packages and their dependencies.)
@@ -564,7 +579,9 @@ func getQuery(path, vers string, forceModulePath bool) (module.Version, error) {
564579
// if found in the current source code.
565580
// Then apply the version to that module.
566581
m, _, err := modload.Import(path)
567-
if err != nil {
582+
if e, ok := err.(*modload.ImportMissingError); ok && e.Module.Path != "" {
583+
m = e.Module
584+
} else if err != nil {
568585
return module.Version{}, err
569586
}
570587
if m.Path == "" {

src/cmd/go/internal/modload/load.go

+13-13
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ func ImportPaths(args []string) []string {
158158
have[path] = true
159159
if path == "all" {
160160
for _, pkg := range loaded.pkgs {
161+
if e, ok := pkg.err.(*ImportMissingError); ok && e.Module.Path == "" {
162+
continue // Package doesn't actually exist, so don't report it.
163+
}
161164
if !have[pkg.path] {
162165
have[pkg.path] = true
163166
final = append(final, pkg.path)
@@ -270,6 +273,9 @@ func loadAll(testAll bool) []string {
270273

271274
var paths []string
272275
for _, pkg := range loaded.pkgs {
276+
if e, ok := pkg.err.(*ImportMissingError); ok && e.Module.Path == "" {
277+
continue // Package doesn't actually exist.
278+
}
273279
paths = append(paths, pkg.path)
274280
}
275281
return paths
@@ -337,21 +343,22 @@ func ModuleUsedDirectly(path string) bool {
337343
return loaded.direct[path]
338344
}
339345

340-
// Lookup returns the source directory and import path for the package at path.
346+
// Lookup returns the source directory, import path, and any loading error for
347+
// the package at path.
341348
// Lookup requires that one of the Load functions in this package has already
342349
// been called.
343350
func Lookup(path string) (dir, realPath string, err error) {
344-
realPath = ImportMap(path)
345-
if realPath == "" {
351+
pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
352+
if !ok {
346353
if isStandardImportPath(path) {
347354
dir := filepath.Join(cfg.GOROOT, "src", path)
348355
if _, err := os.Stat(dir); err == nil {
349356
return dir, path, nil
350357
}
351358
}
352-
return "", "", fmt.Errorf("no such package in module")
359+
return "", "", errMissing
353360
}
354-
return PackageDir(realPath), realPath, nil
361+
return pkg.dir, pkg.path, pkg.err
355362
}
356363

357364
// A loader manages the process of loading information about
@@ -459,9 +466,7 @@ func (ld *loader) load(roots func() []string) {
459466
}
460467
continue
461468
}
462-
if pkg.err != nil {
463-
base.Errorf("go: %s: %s", pkg.stackText(), pkg.err)
464-
}
469+
// Leave other errors for Import or load.Packages to report.
465470
}
466471
base.ExitIfErrors()
467472
if numAdded == 0 {
@@ -560,11 +565,6 @@ func (ld *loader) doPkg(item interface{}) {
560565
var err error
561566
imports, testImports, err = scanDir(pkg.dir, ld.tags)
562567
if err != nil {
563-
if strings.HasPrefix(err.Error(), "no Go ") {
564-
// Don't print about directories with no Go source files.
565-
// Let the eventual real package load do that.
566-
return
567-
}
568568
pkg.err = err
569569
return
570570
}

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

+15-7
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,24 @@ stderr 'cannot find module providing package appengine'
66
! go get x/y.z
77
stderr 'cannot find module providing package x/y.z'
88

9-
# build should skip over appengine imports
10-
! go build
11-
! stderr appengine
9+
# build should report all unsatisfied imports,
10+
# but should be more definitive about non-module import paths
11+
! go build ./useappengine
12+
stderr 'cannot find package'
13+
! go build ./usenonexistent
1214
stderr 'cannot find module providing package nonexistent.rsc.io'
1315

16+
# go mod vendor and go mod tidy should ignore appengine imports.
17+
rm usenonexistent/x.go
18+
go mod tidy
19+
go mod vendor
20+
1421
-- go.mod --
1522
module x
1623

17-
-- x.go --
18-
package x
19-
20-
import _ "appengine"
24+
-- useappengine/x.go --
25+
package useappengine
26+
import _ "appengine" // package does not exist
27+
-- usenonexistent/x.go --
28+
package usenonexistent
2129
import _ "nonexistent.rsc.io" // domain does not exist

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ env GO111MODULE=on
22

33
# @commit should resolve
44

5-
# go get should skip build with no Go files in root
6-
go get golang.org/x/text@14c0d48
7-
8-
# ... and go get should skip build with -m
9-
go get -m golang.org/x/text@14c0d48
10-
115
# golang.org/x/text/language@commit should not resolve with -m,
126
# because that's not a module path.
137
! go get -m golang.org/x/text/language@14c0d48
@@ -17,6 +11,12 @@ go get -m golang.org/x/text@14c0d48
1711
go get -d -x golang.org/x/text/language@14c0d48
1812
! stderr 'compile|cp|gccgo .*language\.a$'
1913

14+
# go get should skip build with no Go files in root
15+
go get golang.org/x/text@14c0d48
16+
17+
# ... and go get should skip build with -m
18+
go get -m golang.org/x/text@14c0d48
19+
2020
# dropping -d, we should see a build.
2121
go get -x golang.org/x/text/language@14c0d48
2222
stderr 'compile|cp|gccgo .*language\.a$'

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

+11-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@ go list -m -f '{{.Path}} {{.Version}}{{if .Indirect}} // indirect{{end}}' all
1111
stdout '^golang.org/x/text [v0-9a-f\.-]+ // indirect'
1212
grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod
1313

14-
# indirect tag should be removed upon seeing direct import
14+
# importing an empty module root as a package makes it direct.
15+
# TODO(bcmills): This doesn't seem correct. Fix is in the next change.
1516
cp $WORK/tmp/usetext.go x.go
17+
go list -e
18+
grep 'golang.org/x/text [v0-9a-f\.-]+$' go.mod
19+
20+
# indirect tag should be removed upon seeing direct import.
21+
cp $WORK/tmp/uselang.go x.go
1622
go list
1723
grep 'rsc.io/quote v1.5.2$' go.mod
1824
grep 'golang.org/x/text [v0-9a-f\.-]+$' go.mod
@@ -24,7 +30,7 @@ grep 'rsc.io/quote v1.5.2$' go.mod
2430
grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod
2531

2632
# requirement should be dropped entirely if not needed
27-
cp $WORK/tmp/usetext.go x.go
33+
cp $WORK/tmp/uselang.go x.go
2834
go mod tidy
2935
! grep rsc.io/quote go.mod
3036
grep 'golang.org/x/text [v0-9a-f\.-]+$' go.mod
@@ -37,6 +43,9 @@ package x
3743
-- $WORK/tmp/usetext.go --
3844
package x
3945
import _ "golang.org/x/text"
46+
-- $WORK/tmp/uselang.go --
47+
package x
48+
import _ "golang.org/x/text/language"
4049
-- $WORK/tmp/usequote.go --
4150
package x
4251
import _ "rsc.io/quote"

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

+29-18
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,35 @@
44
env GO111MODULE=on
55
cd example.com
66

7-
# Listing an otherwise-valid package with an unsatisfied direct import should succeed,
8-
# but name that package in DepsErrors.
9-
! go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}} {{range .DepsErrors}}bad dep: {{.Err}}{{end}}' example.com/direct
10-
stderr example.com[/\\]notfound
7+
# Without -e, listing an otherwise-valid package with an unsatisfied direct import should fail.
8+
# BUG: Today it succeeds.
9+
go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}} {{range .DepsErrors}}bad dep: {{.Err}}{{end}}' example.com/direct
10+
! stdout ^error
11+
stdout 'incomplete'
12+
stdout 'bad dep: .*example.com/notfound'
1113

1214
# Listing with -deps should also fail.
13-
! go list -deps example.com/direct
14-
stderr example.com[/\\]notfound
15+
# BUG: Today, it does not.
16+
# ! go list -deps example.com/direct
17+
# stderr example.com/notfound
18+
go list -deps example.com/direct
19+
stdout example.com/notfound
1520

1621

1722
# Listing an otherwise-valid package that imports some *other* package with an
18-
# unsatisfied import should also succeed.
19-
# NOTE: This behavior differs between GOPATH mode and module mode.
20-
! go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}} {{range .DepsErrors}}bad dep: {{.Err}}{{end}}' example.com/indirect
21-
stderr example.com[/\\]notfound
23+
# unsatisfied import should also fail.
24+
# BUG: Today, it succeeds.
25+
go list -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}} {{range .DepsErrors}}bad dep: {{.Err}}{{end}}' example.com/indirect
26+
! stdout ^error
27+
stdout incomplete
28+
stdout 'bad dep: .*example.com/notfound'
2229

2330
# Again, -deps should fail.
24-
! go list -deps example.com/indirect
25-
stderr example.com[/\\]notfound
31+
# BUG: Again, it does not.
32+
# ! go list -deps example.com/indirect
33+
# stderr example.com/notfound
34+
go list -deps example.com/indirect
35+
stdout example.com/notfound
2636

2737

2838
# Listing the missing dependency directly should fail outright...
@@ -32,16 +42,17 @@ stderr 'cannot find module providing package example.com/notfound'
3242
! stdout incomplete
3343

3444
# ...but listing with -e should succeed.
35-
# BUG: Today, it fails.
36-
! go list -e -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}}' example.com/notfound
37-
stderr example.com[/\\]notfound
45+
go list -e -f '{{if .Error}}error{{end}} {{if .Incomplete}}incomplete{{end}}' example.com/notfound
46+
stdout error
47+
stdout incomplete
3848

3949

4050
# The pattern "all" should match only packages that acutally exist,
4151
# ignoring those whose existence is merely implied by imports.
42-
# BUG: Today, `go list -e` fails if there are any unresolved imports.
43-
! go list -e -f '{{.ImportPath}}' all
44-
stderr example.com[/\\]notfound
52+
go list -e -f '{{.ImportPath}}' all
53+
stdout example.com/direct
54+
stdout example.com/indirect
55+
! stdout example.com/notfound
4556

4657

4758
-- example.com/go.mod --

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
env GO111MODULE=on
22

33
# -mod=readonly must not resolve missing modules nor update go.mod
4+
#
5+
# TODO(bcmills): 'go list' should suffice, but today it does not fail due to
6+
# unresolved imports. When that is fixed, use 'go list' instead of 'go list all'.
47
env GOFLAGS=-mod=readonly
58
go mod edit -fmt
69
cp go.mod go.mod.empty
7-
! go list
10+
! go list all
811
stderr 'import lookup disabled by -mod=readonly'
912
cmp go.mod go.mod.empty
1013

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

+6
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ package m
155155

156156
import _ "appengine"
157157
import _ "appengine/datastore"
158+
-- nonexistent.go --
159+
// +build alternatereality
160+
161+
package m
162+
163+
import _ "nonexistent.rsc.io"
158164
-- mypkg/go.mod --
159165
module me
160166
-- mypkg/mydir/d.go --

0 commit comments

Comments
 (0)