From 223331fc0cf5b23fbb9999eb1164b23695ef612a Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Tue, 22 Dec 2020 16:57:46 -0500 Subject: [PATCH] cmd/go/internal/modload: add hint for missing implicit dependency By default (and with -mod=readonly), the go command imports an error if a package provided by an implicitly required module is imported by a package in the main module. This import requires an update to go.mod: the module must be required explicitly. The package loader now provides a hint that 'go get' should be run on the importing package. This is preferred to 'go get' on the imported package, since that would add an "// indirect" requirement. For #43131 Change-Id: I0b353ce8ac8c4ddf1a9863544dfaf6c1964daf42 Reviewed-on: https://go-review.googlesource.com/c/go/+/279528 Trust: Jay Conrod Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills --- src/cmd/go/internal/modload/load.go | 11 ++- .../script/mod_get_promote_implicit.txt | 82 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/cmd/go/testdata/script/mod_get_promote_implicit.txt diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index a0f93d028a..27f47fad4d 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -863,12 +863,21 @@ func loadFromRoots(params loaderParams) *loader { for _, pkg := range ld.pkgs { if pkg.mod == Target { for _, dep := range pkg.imports { - if dep.mod.Path != "" { + if dep.mod.Path != "" && dep.mod.Path != Target.Path && index != nil { + _, explicit := index.require[dep.mod] + if allowWriteGoMod && cfg.BuildMod == "readonly" && !explicit { + // TODO(#40775): attach error to package instead of using + // 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) + } ld.direct[dep.mod.Path] = true } } } } + base.ExitIfErrors() // If we didn't scan all of the imports from the main module, or didn't use // imports.AnyTags, then we didn't necessarily load every package that diff --git a/src/cmd/go/testdata/script/mod_get_promote_implicit.txt b/src/cmd/go/testdata/script/mod_get_promote_implicit.txt new file mode 100644 index 0000000000..33f6a299e2 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_promote_implicit.txt @@ -0,0 +1,82 @@ +cp go.mod.orig go.mod + +# If we list a package in an implicit dependency imported from the main module, +# we should get an error because the dependency should have an explicit +# requirement. +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$' + +# We can promote the implicit requirement by getting the importing package, +# as hinted. +go get -d m/use-indirect +cmp go.mod go.mod.use +cp go.mod.orig go.mod + +-- go.mod.orig -- +module m + +go 1.16 + +require direct v1.0.0 + +replace ( + direct v1.0.0 => ./direct + indirect-with-pkg v1.0.0 => ./indirect-with-pkg + indirect-without-pkg v1.0.0 => ./indirect-without-pkg +) +-- go.mod.use -- +module m + +go 1.16 + +require ( + direct v1.0.0 + indirect-with-pkg v1.0.0 +) + +replace ( + direct v1.0.0 => ./direct + indirect-with-pkg v1.0.0 => ./indirect-with-pkg + indirect-without-pkg v1.0.0 => ./indirect-without-pkg +) +-- go.mod.indirect -- +module m + +go 1.16 + +require ( + direct v1.0.0 + indirect-with-pkg v1.0.0 // indirect + indirect-without-pkg v1.0.0 // indirect +) + +replace ( + direct v1.0.0 => ./direct + indirect-with-pkg v1.0.0 => ./indirect-with-pkg + indirect-without-pkg v1.0.0 => ./indirect-without-pkg +) +-- use-indirect/use-indirect.go -- +package use + +import _ "indirect-with-pkg" +-- direct/go.mod -- +module direct + +go 1.16 + +require ( + indirect-with-pkg v1.0.0 + indirect-without-pkg v1.0.0 +) +-- indirect-with-pkg/go.mod -- +module indirect-with-pkg + +go 1.16 +-- indirect-with-pkg/p.go -- +package p +-- indirect-without-pkg/go.mod -- +module indirect-without-pkg + +go 1.16 -- 2.48.1