]> Cypherpunks repositories - gostls13.git/commitdiff
modload: provide a clearer error for standard library packages from newer releases
authorJeremy Brewer <jeremybrewer@google.com>
Tue, 20 Sep 2022 00:09:56 +0000 (20:09 -0400)
committerGopher Robot <gobot@golang.org>
Fri, 7 Oct 2022 20:20:51 +0000 (20:20 +0000)
An older version of go compiling a main module that references a
standard library package from a newer release (e.g. net/netip added in
go 1.18) currently produces a confusing error message. This changes adds
a new error message including go version diagnostics.

Fixes #48966

Change-Id: I1e8319dafcf1f67d1b1ca869fe84190c3b3f3c3e
Reviewed-on: https://go-review.googlesource.com/c/go/+/432075
Auto-Submit: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/cmd/go/internal/modload/import.go
src/cmd/go/internal/modload/load.go
src/cmd/go/testdata/script/mod_load_missing_std.txt [new file with mode: 0644]

index d1ac274a28e8dac5c5ddeda35b984dd717749b8d..b314656b96a9534e229b0b827ca167f20da3d3ba 100644 (file)
@@ -41,6 +41,10 @@ type ImportMissingError struct {
        // modules.
        isStd bool
 
+       // importerGoVersion is the version the module containing the import error
+       // specified. It is only set when isStd is true.
+       importerGoVersion string
+
        // replaced the highest replaced version of the module where the replacement
        // contains the package. replaced is only set if the replacement is unused.
        replaced module.Version
@@ -53,7 +57,11 @@ type ImportMissingError struct {
 func (e *ImportMissingError) Error() string {
        if e.Module.Path == "" {
                if e.isStd {
-                       return fmt.Sprintf("package %s is not in GOROOT (%s)", e.Path, filepath.Join(cfg.GOROOT, "src", e.Path))
+                       msg := fmt.Sprintf("package %s is not in GOROOT (%s)", e.Path, filepath.Join(cfg.GOROOT, "src", e.Path))
+                       if e.importerGoVersion != "" {
+                               msg += fmt.Sprintf("\nnote: imported by a module that requires go %s", e.importerGoVersion)
+                       }
+                       return msg
                }
                if e.QueryErr != nil && e.QueryErr != ErrNoModRoot {
                        return fmt.Sprintf("cannot find module providing package %s: %v", e.Path, e.QueryErr)
index afd6c80370c745cded597e9b09af5024dcc35274..09572bf1b19251740ad0fd92f51a920fb6ac064d 100644 (file)
@@ -984,7 +984,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
        if ld.GoVersion == "" {
                ld.GoVersion = MainModules.GoVersion()
 
-               if ld.Tidy && semver.Compare("v"+ld.GoVersion, "v"+LatestGoVersion()) > 0 {
+               if ld.Tidy && versionLess(LatestGoVersion(), ld.GoVersion) {
                        ld.errorf("go: go.mod file indicates go %s, but maximum version supported by tidy is %s\n", ld.GoVersion, LatestGoVersion())
                        base.ExitIfErrors()
                }
@@ -993,7 +993,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
        if ld.Tidy {
                if ld.TidyCompatibleVersion == "" {
                        ld.TidyCompatibleVersion = priorGoVersion(ld.GoVersion)
-               } else if semver.Compare("v"+ld.TidyCompatibleVersion, "v"+ld.GoVersion) > 0 {
+               } else if versionLess(ld.GoVersion, ld.TidyCompatibleVersion) {
                        // Each version of the Go toolchain knows how to interpret go.mod and
                        // go.sum files produced by all previous versions, so a compatibility
                        // version higher than the go.mod version adds nothing.
@@ -1184,11 +1184,19 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
                        }
                }
 
-               if ld.SilencePackageErrors {
-                       continue
+               if stdErr := (*ImportMissingError)(nil); errors.As(pkg.err, &stdErr) && stdErr.isStd {
+                       // Add importer go version information to import errors of standard
+                       // library packages arising from newer releases.
+                       if importer := pkg.stack; importer != nil {
+                               if v, ok := rawGoVersion.Load(importer.mod); ok && versionLess(LatestGoVersion(), v.(string)) {
+                                       stdErr.importerGoVersion = v.(string)
+                               }
+                       }
+                       if ld.SilenceMissingStdImports {
+                               continue
+                       }
                }
-               if stdErr := (*ImportMissingError)(nil); errors.As(pkg.err, &stdErr) &&
-                       stdErr.isStd && ld.SilenceMissingStdImports {
+               if ld.SilencePackageErrors {
                        continue
                }
                if ld.SilenceNoGoErrors && errors.Is(pkg.err, imports.ErrNoGo) {
@@ -1202,6 +1210,12 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
        return ld
 }
 
+// versionLess returns whether a < b according to semantic version precedence.
+// Both strings are interpreted as go version strings, e.g. "1.19".
+func versionLess(a, b string) bool {
+       return semver.Compare("v"+a, "v"+b) < 0
+}
+
 // updateRequirements ensures that ld.requirements is consistent with the
 // information gained from ld.pkgs.
 //
diff --git a/src/cmd/go/testdata/script/mod_load_missing_std.txt b/src/cmd/go/testdata/script/mod_load_missing_std.txt
new file mode 100644 (file)
index 0000000..bd2508a
--- /dev/null
@@ -0,0 +1,17 @@
+# Go should indicate the version the module requires when a standard library
+# import is missing. See golang.org/issue/48966.
+
+! go build .
+stderr '^main.go:3:8: package nonexistent is not in GOROOT \(.*\)$'
+stderr '^note: imported by a module that requires go 1.99999$'
+
+-- go.mod --
+module example
+
+go 1.99999
+-- main.go --
+package main
+
+import _ "nonexistent"
+
+func main() {}