]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go/internal/modload: add hint for missing implicit dependency
authorJay Conrod <jayconrod@google.com>
Tue, 22 Dec 2020 21:57:46 +0000 (16:57 -0500)
committerJay Conrod <jayconrod@google.com>
Tue, 22 Dec 2020 23:39:46 +0000 (23:39 +0000)
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 <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>
src/cmd/go/internal/modload/load.go
src/cmd/go/testdata/script/mod_get_promote_implicit.txt [new file with mode: 0644]

index a0f93d028a54c1d553587dc9bdbc3ba8b9eb781d..27f47fad4d3558926518499e8127b97a7e6020b5 100644 (file)
@@ -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 (file)
index 0000000..33f6a29
--- /dev/null
@@ -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