]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: make hints in error messages more consistent
authorJay Conrod <jayconrod@google.com>
Thu, 7 Jan 2021 16:14:06 +0000 (11:14 -0500)
committerJay Conrod <jayconrod@google.com>
Fri, 8 Jan 2021 20:19:51 +0000 (20:19 +0000)
* All commands the user can run to fix the problem now appear alone on
  a separate line after a tab.
* Removed -d from 'go get' commands.
* Replaced 'go mod tidy' with 'go mod download $modpath' when a
  package might be provided by a module missing a sum.
* Errors about 'path@version' syntax are more explicit.

Fixes #29415
Fixes #42087
Fixes #43430
Fixes #43523

Change-Id: I4427c2c4506a727a2c727d652fd2d506bb134d3b
Reviewed-on: https://go-review.googlesource.com/c/go/+/282121
Trust: Jay Conrod <jayconrod@google.com>
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
17 files changed:
src/cmd/go/go_test.go
src/cmd/go/internal/get/get.go
src/cmd/go/internal/load/pkg.go
src/cmd/go/internal/modget/get.go
src/cmd/go/internal/modload/import.go
src/cmd/go/internal/modload/init.go
src/cmd/go/internal/modload/load.go
src/cmd/go/internal/modload/vendor.go
src/cmd/go/internal/test/testflag.go
src/cmd/go/testdata/script/mod_get_promote_implicit.txt
src/cmd/go/testdata/script/mod_get_retract.txt
src/cmd/go/testdata/script/mod_invalid_path.txt
src/cmd/go/testdata/script/mod_sum_ambiguous.txt
src/cmd/go/testdata/script/mod_sum_readonly.txt
src/cmd/go/testdata/script/mod_vendor_auto.txt
src/cmd/go/testdata/script/mod_versions.txt
src/cmd/go/testdata/script/test_flag.txt

index c472620db2960008d1116215c5587b90b87802ef..3cd3454d5a0c89a75c41977fd72e79cff2ce448e 100644 (file)
@@ -2655,12 +2655,12 @@ func TestBadCommandLines(t *testing.T) {
        tg.tempFile("src/@x/x.go", "package x\n")
        tg.setenv("GOPATH", tg.path("."))
        tg.runFail("build", "@x")
-       tg.grepStderr("invalid input directory name \"@x\"|cannot use path@version syntax", "did not reject @x directory")
+       tg.grepStderr("invalid input directory name \"@x\"|can only use path@version syntax with 'go get' and 'go install' in module-aware mode", "did not reject @x directory")
 
        tg.tempFile("src/@x/y/y.go", "package y\n")
        tg.setenv("GOPATH", tg.path("."))
        tg.runFail("build", "@x/y")
-       tg.grepStderr("invalid import path \"@x/y\"|cannot use path@version syntax", "did not reject @x/y import path")
+       tg.grepStderr("invalid import path \"@x/y\"|can only use path@version syntax with 'go get' and 'go install' in module-aware mode", "did not reject @x/y import path")
 
        tg.tempFile("src/-x/x.go", "package x\n")
        tg.setenv("GOPATH", tg.path("."))
index 94a42c4f73a2b71018e968bc542700db4ce65130..38ff3823f22b521f46682841e02564afd6de923d 100644 (file)
@@ -202,7 +202,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
 func downloadPaths(patterns []string) []string {
        for _, arg := range patterns {
                if strings.Contains(arg, "@") {
-                       base.Fatalf("go: cannot use path@version syntax in GOPATH mode")
+                       base.Fatalf("go: can only use path@version syntax with 'go get' and 'go install' in module-aware mode")
                        continue
                }
 
index 855f9698a2c86cc7c95a91498a2813d72e8296d4..cffc8fcefa5b9925ba253d8256e4f2fa55275a6e 100644 (file)
@@ -769,11 +769,7 @@ func loadPackageData(path, parentPath, parentDir, parentRoot string, parentIsStd
        }
 
        if strings.Contains(path, "@") {
-               if cfg.ModulesEnabled {
-                       return nil, false, errors.New("can only use path@version syntax with 'go get'")
-               } else {
-                       return nil, false, errors.New("cannot use path@version syntax in GOPATH mode")
-               }
+               return nil, false, errors.New("can only use path@version syntax with 'go get' and 'go install' in module-aware mode")
        }
 
        // Determine canonical package path and directory.
index 8463ec4e9c7e3f4b0978ec2dcc242d6ecdd45c44..0770b601c0af14a87d46de3d4997d324abe603d5 100644 (file)
@@ -1558,7 +1558,7 @@ func (r *resolver) checkPackagesAndRetractions(ctx context.Context, pkgPatterns
                }
        }
        if retractPath != "" {
-               fmt.Fprintf(os.Stderr, "go: run 'go get %s@latest' to switch to the latest unretracted version\n", retractPath)
+               fmt.Fprintf(os.Stderr, "go: to switch to the latest unretracted version, run:\n\tgo get %s@latest", retractPath)
        }
 }
 
index 055878c528a9689aa017c5ca6de99e3def59cf52..9925d5b9054a587b597d606a1b24e472f23d6a1d 100644 (file)
@@ -134,6 +134,7 @@ func (e *AmbiguousImportError) Error() string {
 // for its .zip file.
 type ImportMissingSumError struct {
        importPath   string
+       modPaths     []string
        found, inAll bool
 }
 
@@ -145,7 +146,7 @@ func (e *ImportMissingSumError) Error() string {
                message = fmt.Sprintf("missing go.sum entry for module providing package %s", e.importPath)
        }
        if e.inAll {
-               return message + "; to add it:\n\tgo mod tidy"
+               return message + fmt.Sprintf("; to add it:\n\tgo mod download %s", strings.Join(e.modPaths, " "))
        }
        return message
 }
@@ -238,7 +239,7 @@ func importFromBuildList(ctx context.Context, path string, buildList []module.Ve
        // Check each module on the build list.
        var dirs []string
        var mods []module.Version
-       haveSumErr := false
+       var sumErrModPaths []string
        for _, m := range buildList {
                if !maybeInModule(path, m.Path) {
                        // Avoid possibly downloading irrelevant modules.
@@ -251,8 +252,9 @@ func importFromBuildList(ctx context.Context, path string, buildList []module.Ve
                                // We are missing a sum needed to fetch a module in the build list.
                                // We can't verify that the package is unique, and we may not find
                                // the package at all. Keep checking other modules to decide which
-                               // error to report.
-                               haveSumErr = true
+                               // error to report. Multiple sums may be missing if we need to look in
+                               // multiple nested modules to resolve the import; we'll report them all.
+                               sumErrModPaths = append(sumErrModPaths, m.Path)
                                continue
                        }
                        // Report fetch error.
@@ -273,8 +275,8 @@ func importFromBuildList(ctx context.Context, path string, buildList []module.Ve
        if len(mods) > 1 {
                return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
        }
-       if haveSumErr {
-               return module.Version{}, "", &ImportMissingSumError{importPath: path, found: len(mods) > 0}
+       if len(sumErrModPaths) > 0 {
+               return module.Version{}, "", &ImportMissingSumError{importPath: path, modPaths: sumErrModPaths, found: len(mods) > 0}
        }
        if len(mods) == 1 {
                return mods[0], dirs[0], nil
index 1a51c58bf2921e3928735c7a692ebdea72fd7e46..bc8d17e0a5fb5934d93a09025a186d21a0cccb6b 100644 (file)
@@ -380,7 +380,7 @@ func LoadModFile(ctx context.Context) {
 
        if f.Module == nil {
                // No module declaration. Must add module path.
-               base.Fatalf("go: no module declaration in go.mod.\n\tRun 'go mod edit -module=example.com/mod' to specify the module path.")
+               base.Fatalf("go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
        }
 
        if err := checkModulePathLax(f.Module.Mod.Path); err != nil {
index ae5b8ef6abd3386ca101ead354393d80794c4157..cd36da6a87d584e1852b1b9dc8f6a6a8e7647130 100644 (file)
@@ -868,7 +868,7 @@ func loadFromRoots(params loaderParams) *loader {
                                                // base.Errorf. Ideally, 'go list' should not fail because of this,
                                                // but today, LoadPackages calls WriteGoMod unconditionally, which
                                                // would fail with a less clear message.
-                                               base.Errorf("go: %[1]s: package %[2]s imported from implicitly required module; try 'go get -d %[1]s' to add missing requirements", pkg.path, dep.path)
+                                               base.Errorf("go: %[1]s: package %[2]s imported from implicitly required module; to add missing requirements, run:\n\tgo get %[2]s@%[3]s", pkg.path, dep.path, dep.mod.Version)
                                        }
                                        ld.direct[dep.mod.Path] = true
                                }
index 80d49053c6c6f0f2b9e91f92dbaebc27a164e5a2..d8fd91f1fea2b20339b4c2f61a1a77f5da6ab15f 100644 (file)
@@ -214,6 +214,6 @@ func checkVendorConsistency() {
        }
 
        if vendErrors.Len() > 0 {
-               base.Fatalf("go: inconsistent vendoring in %s:%s\n\nrun 'go mod vendor' to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory", modRoot, vendErrors)
+               base.Fatalf("go: inconsistent vendoring in %s:%s\n\n\tTo ignore the vendor directory, use -mod=readonly or -mod=mod.\n\tTo sync the vendor directory, run:\n\t\tgo mod vendor", modRoot, vendErrors)
        }
 }
index d2671ff5a7925c63c25df4be9b5cae5be9822c21..10e6604da5f44b3b1c41f56f0653a667b7019196 100644 (file)
@@ -325,7 +325,7 @@ func testFlags(args []string) (packageNames, passToTest []string) {
                if !testC {
                        buildFlag = "-i"
                }
-               fmt.Fprintf(os.Stderr, "flag %s is not a 'go test' flag (unknown flags cannot be used with %s)\n", firstUnknownFlag, buildFlag)
+               fmt.Fprintf(os.Stderr, "go test: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag)
                exitWithUsage()
        }
 
index c64e0c0f7068a777253d99d9d7cb05583e208c54..10ca6594e4c6e33e8abd5c1b38624d50e04b9ff7 100644 (file)
@@ -6,10 +6,12 @@ cp go.mod.orig go.mod
 go list -m indirect-with-pkg
 stdout '^indirect-with-pkg v1.0.0 => ./indirect-with-pkg$'
 ! go list ./use-indirect
-stderr '^go: m/use-indirect: package indirect-with-pkg imported from implicitly required module; try ''go get -d m/use-indirect'' to add missing requirements$'
+stderr '^go: m/use-indirect: package indirect-with-pkg imported from implicitly required module; to add missing requirements, run:\n\tgo get indirect-with-pkg@v1.0.0$'
 
-# We can promote the implicit requirement by getting the importing package,
-# as hinted.
+# We can promote the implicit requirement by getting the importing package.
+# NOTE: the hint recommends getting the imported package (tested below) since
+# it's more obvious and doesn't require -d. However, that adds an '// indirect'
+# comment on the requirement.
 go get -d m/use-indirect
 cmp go.mod go.mod.use
 cp go.mod.orig go.mod
@@ -17,6 +19,8 @@ cp go.mod.orig go.mod
 # We can also promote implicit requirements using 'go get' on them, or their
 # packages. This gives us "// indirect" requirements, since 'go get' doesn't
 # know they're needed by the main module. See #43131 for the rationale.
+# The hint above recommends this because it's more obvious usage and doesn't
+# require the -d flag.
 go get -d indirect-with-pkg indirect-without-pkg
 cmp go.mod go.mod.indirect
 
index 6e328eb5929292994e18cc2cd6b5b1bd0631eb6c..fe0ac886295e8f89ea11876fbedd702cda690ae0 100644 (file)
@@ -11,7 +11,7 @@ cp go.mod.orig go.mod
 go mod edit -require example.com/retract/self/prev@v1.9.0
 go get -d example.com/retract/self/prev
 stderr '^go: warning: example.com/retract/self/prev@v1.9.0: retracted by module author: self$'
-stderr '^go: run ''go get example.com/retract/self/prev@latest'' to switch to the latest unretracted version$'
+stderr '^go: to switch to the latest unretracted version, run:\n\tgo get example.com/retract/self/prev@latest$'
 go list -m example.com/retract/self/prev
 stdout '^example.com/retract/self/prev v1.9.0$'
 
index 05a513357176cb8d301aecdb494e4ca2a90f1ba9..667828839fdd8cc3cee2a01788097495d4ad88ee 100644 (file)
@@ -3,7 +3,7 @@
 # Test that go list fails on a go.mod with no module declaration.
 cd $WORK/gopath/src/mod
 ! go list .
-stderr '^go: no module declaration in go.mod.\n\tRun ''go mod edit -module=example.com/mod'' to specify the module path.$'
+stderr '^go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod$'
 
 # Test that go mod init in GOPATH doesn't add a module declaration
 # with a path that can't possibly be a module path, because
index 209367181d4203d84917a5cafe0f07e4755d6ad5..5344dc00297da90b6cc6b93d2b44e6b15744afb3 100644 (file)
@@ -25,13 +25,17 @@ cp go.sum.a-only go.sum
 ! go list example.com/ambiguous/a/b
 stderr '^missing go.sum entry needed to verify package example.com/ambiguous/a/b is provided by exactly one module$'
 ! go list -deps .
-stderr '^use.go:3:8: missing go.sum entry needed to verify package example.com/ambiguous/a/b is provided by exactly one module; to add it:\n\tgo mod tidy$'
+stderr '^use.go:3:8: missing go.sum entry needed to verify package example.com/ambiguous/a/b is provided by exactly one module; to add it:\n\tgo mod download example.com/ambiguous/a/b$'
 
 cp go.sum.b-only go.sum
 ! go list example.com/ambiguous/a/b
 stderr '^missing go.sum entry for module providing package example.com/ambiguous/a/b$'
 ! go list -deps .
-stderr '^use.go:3:8: missing go.sum entry for module providing package example.com/ambiguous/a/b; to add it:\n\tgo mod tidy$'
+stderr '^use.go:3:8: missing go.sum entry for module providing package example.com/ambiguous/a/b; to add it:\n\tgo mod download example.com/ambiguous/a$'
+
+cp go.sum.buildlist-only go.sum
+! go list -deps .
+stderr '^use.go:3:8: missing go.sum entry for module providing package example.com/ambiguous/a/b; to add it:\n\tgo mod download example.com/ambiguous/a example.com/ambiguous/a/b$'
 
 -- go.mod --
 module m
index 866f4c1ae45bf2235fc6fc393e1f5a27bb64d240..00b4d7b5d2e912c47526c4d876baa949903fe90b 100644 (file)
@@ -47,7 +47,7 @@ stderr '^missing go.sum entry for module providing package rsc.io/quote$'
 # a package that imports it without that error.
 go list -e -deps -f '{{.ImportPath}}{{with .Error}} {{.Err}}{{end}}' .
 stdout '^m$'
-stdout '^rsc.io/quote missing go.sum entry for module providing package rsc.io/quote; to add it:\n\tgo mod tidy$'
+stdout '^rsc.io/quote missing go.sum entry for module providing package rsc.io/quote; to add it:\n\tgo mod download rsc.io/quote$'
 ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip
 
 # go.sum should not have been written.
index 1b362eda0bdf0b5cd8b82f55afbb88f34cfd1e91..b0ea907206a44902b6001a8df73bd80d283f7893 100644 (file)
@@ -66,7 +66,7 @@ stderr '^go: inconsistent vendoring in '$WORK[/\\]auto':$'
 stderr '^\texample.com/printversion@v1.0.0: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt'
 stderr '^\texample.com/unused: is replaced in go.mod, but not marked as replaced in vendor/modules.txt'
 stderr '^\texample.com/version@v1.2.0: is replaced in go.mod, but not marked as replaced in vendor/modules.txt'
-stderr '\n\nrun .go mod vendor. to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory$'
+stderr '^\tTo ignore the vendor directory, use -mod=readonly or -mod=mod.\n\tTo sync the vendor directory, run:\n\t\tgo mod vendor$'
 
 # Module-specific subcommands should continue to load the full module graph.
 go mod graph
@@ -135,7 +135,7 @@ stderr '^go: inconsistent vendoring in '$WORK[/\\]auto':$'
 stderr '^\texample.com/printversion@v1.0.0: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt'
 stderr '^\texample.com/unused: is replaced in go.mod, but not marked as replaced in vendor/modules.txt'
 stderr '^\texample.com/version@v1.2.0: is replaced in go.mod, but not marked as replaced in vendor/modules.txt'
-stderr '\n\nrun .go mod vendor. to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory$'
+stderr '^\tTo ignore the vendor directory, use -mod=readonly or -mod=mod.\n\tTo sync the vendor directory, run:\n\t\tgo mod vendor$'
 
 # If -mod=vendor is set, limited consistency checks should apply even when
 # the go version is 1.13 or earlier.
@@ -151,7 +151,7 @@ cp $WORK/modules-bad-1.13.txt vendor/modules.txt
 ! go list -mod=vendor -f {{.Dir}} -tags tools all
 stderr '^go: inconsistent vendoring in '$WORK[/\\]auto':$'
 stderr '^\texample.com/printversion@v1.0.0: is explicitly required in go.mod, but vendor/modules.txt indicates example.com/printversion@v1.1.0$'
-stderr '\n\nrun .go mod vendor. to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory$'
+stderr '^\tTo ignore the vendor directory, use -mod=readonly or -mod=mod.\n\tTo sync the vendor directory, run:\n\t\tgo mod vendor$'
 
 # If the go version is still 1.13, 'go mod vendor' should write a
 # matching vendor/modules.txt containing the corrected 1.13 data.
index fd5e5c589d2237490b055de861603e1ae6938e91..9e6322bae117f778289477642521666befe32ab4 100644 (file)
@@ -1,14 +1,14 @@
 # Test rejection of pkg@version in GOPATH mode.
 env GO111MODULE=off
 ! go get rsc.io/quote@v1.5.1
-stderr 'cannot use path@version syntax in GOPATH mode'
+stderr '^go: can only use path@version syntax with ''go get'' and ''go install'' in module-aware mode$'
 ! go build rsc.io/quote@v1.5.1
-stderr 'cannot use path@version syntax in GOPATH mode'
+stderr '^package rsc.io/quote@v1.5.1: can only use path@version syntax with ''go get'' and ''go install'' in module-aware mode$'
 
 env GO111MODULE=on
 cd x
 ! go build rsc.io/quote@v1.5.1
-stderr 'can only use path@version syntax with ''go get'''
+stderr '^package rsc.io/quote@v1.5.1: can only use path@version syntax with ''go get'' and ''go install'' in module-aware mode$'
 
 -- x/go.mod --
 module x
index ec88d38cbe805f96194d13670e00b4fcd145e837..0142b3f308f3b28005cb05f39819ca7073fa2a67 100644 (file)
@@ -9,13 +9,13 @@ go test -count=1 -custom -args -v=7
 # However, it should be an error to use custom flags when -i or -c are used,
 # since we know for sure that no test binary will run at all.
 ! go test -i -custom
-stderr '^flag -custom is not a ''go test'' flag \(unknown flags cannot be used with -i\)$'
+stderr '^go test: unknown flag -custom cannot be used with -i$'
 ! go test -c -custom
-stderr '^flag -custom is not a ''go test'' flag \(unknown flags cannot be used with -c\)$'
+stderr '^go test: unknown flag -custom cannot be used with -c$'
 
 # The same should apply even if -c or -i come after a custom flag.
 ! go test -custom -c
-stderr '^flag -custom is not a ''go test'' flag \(unknown flags cannot be used with -c\)$'
+stderr '^go test: unknown flag -custom cannot be used with -c$'
 
 -- go.mod --
 module m