Skip to content

Commit 40c7e94

Browse files
lazerkatgopherbot
authored andcommitted
modload: provide a clearer error for standard library packages from newer releases
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 <[email protected]> Run-TryBot: Bryan Mills <[email protected]> Reviewed-by: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 9be0991 commit 40c7e94

File tree

3 files changed

+46
-7
lines changed

3 files changed

+46
-7
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ type ImportMissingError struct {
4141
// modules.
4242
isStd bool
4343

44+
// importerGoVersion is the version the module containing the import error
45+
// specified. It is only set when isStd is true.
46+
importerGoVersion string
47+
4448
// replaced the highest replaced version of the module where the replacement
4549
// contains the package. replaced is only set if the replacement is unused.
4650
replaced module.Version
@@ -53,7 +57,11 @@ type ImportMissingError struct {
5357
func (e *ImportMissingError) Error() string {
5458
if e.Module.Path == "" {
5559
if e.isStd {
56-
return fmt.Sprintf("package %s is not in GOROOT (%s)", e.Path, filepath.Join(cfg.GOROOT, "src", e.Path))
60+
msg := fmt.Sprintf("package %s is not in GOROOT (%s)", e.Path, filepath.Join(cfg.GOROOT, "src", e.Path))
61+
if e.importerGoVersion != "" {
62+
msg += fmt.Sprintf("\nnote: imported by a module that requires go %s", e.importerGoVersion)
63+
}
64+
return msg
5765
}
5866
if e.QueryErr != nil && e.QueryErr != ErrNoModRoot {
5967
return fmt.Sprintf("cannot find module providing package %s: %v", e.Path, e.QueryErr)

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

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
984984
if ld.GoVersion == "" {
985985
ld.GoVersion = MainModules.GoVersion()
986986

987-
if ld.Tidy && semver.Compare("v"+ld.GoVersion, "v"+LatestGoVersion()) > 0 {
987+
if ld.Tidy && versionLess(LatestGoVersion(), ld.GoVersion) {
988988
ld.errorf("go: go.mod file indicates go %s, but maximum version supported by tidy is %s\n", ld.GoVersion, LatestGoVersion())
989989
base.ExitIfErrors()
990990
}
@@ -993,7 +993,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
993993
if ld.Tidy {
994994
if ld.TidyCompatibleVersion == "" {
995995
ld.TidyCompatibleVersion = priorGoVersion(ld.GoVersion)
996-
} else if semver.Compare("v"+ld.TidyCompatibleVersion, "v"+ld.GoVersion) > 0 {
996+
} else if versionLess(ld.GoVersion, ld.TidyCompatibleVersion) {
997997
// Each version of the Go toolchain knows how to interpret go.mod and
998998
// go.sum files produced by all previous versions, so a compatibility
999999
// version higher than the go.mod version adds nothing.
@@ -1184,11 +1184,19 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
11841184
}
11851185
}
11861186

1187-
if ld.SilencePackageErrors {
1188-
continue
1187+
if stdErr := (*ImportMissingError)(nil); errors.As(pkg.err, &stdErr) && stdErr.isStd {
1188+
// Add importer go version information to import errors of standard
1189+
// library packages arising from newer releases.
1190+
if importer := pkg.stack; importer != nil {
1191+
if v, ok := rawGoVersion.Load(importer.mod); ok && versionLess(LatestGoVersion(), v.(string)) {
1192+
stdErr.importerGoVersion = v.(string)
1193+
}
1194+
}
1195+
if ld.SilenceMissingStdImports {
1196+
continue
1197+
}
11891198
}
1190-
if stdErr := (*ImportMissingError)(nil); errors.As(pkg.err, &stdErr) &&
1191-
stdErr.isStd && ld.SilenceMissingStdImports {
1199+
if ld.SilencePackageErrors {
11921200
continue
11931201
}
11941202
if ld.SilenceNoGoErrors && errors.Is(pkg.err, imports.ErrNoGo) {
@@ -1202,6 +1210,12 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
12021210
return ld
12031211
}
12041212

1213+
// versionLess returns whether a < b according to semantic version precedence.
1214+
// Both strings are interpreted as go version strings, e.g. "1.19".
1215+
func versionLess(a, b string) bool {
1216+
return semver.Compare("v"+a, "v"+b) < 0
1217+
}
1218+
12051219
// updateRequirements ensures that ld.requirements is consistent with the
12061220
// information gained from ld.pkgs.
12071221
//
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Go should indicate the version the module requires when a standard library
2+
# import is missing. See golang.org/issue/48966.
3+
4+
! go build .
5+
stderr '^main.go:3:8: package nonexistent is not in GOROOT \(.*\)$'
6+
stderr '^note: imported by a module that requires go 1.99999$'
7+
8+
-- go.mod --
9+
module example
10+
11+
go 1.99999
12+
-- main.go --
13+
package main
14+
15+
import _ "nonexistent"
16+
17+
func main() {}

0 commit comments

Comments
 (0)