Skip to content

Commit 4fdb98d

Browse files
author
Jay Conrod
committed
cmd/go: save sums for zips needed to diagnose ambiguous imports
Previously, we would retain entries in go.sum for .mod files in the module graph (reachable from the main module) and for .zip files of modules providing packages. This isn't quite enough: when we load a package, we need the content of each module in the build list that *could* provide the package (that is, each module whose path is a prefix of the package's path) so we can diagnose ambiguous imports. For #33008 Change-Id: I0b4d9d68c1f4ca382f0983a3a7e537764f35c3aa Reviewed-on: https://go-review.googlesource.com/c/go/+/262781 Reviewed-by: Michael Matloob <[email protected]> Reviewed-by: Bryan C. Mills <[email protected]> Trust: Jay Conrod <[email protected]>
1 parent 5cd4390 commit 4fdb98d

File tree

4 files changed

+138
-27
lines changed

4 files changed

+138
-27
lines changed

src/cmd/go/internal/modload/init.go

+60-27
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"path/filepath"
1919
"strconv"
2020
"strings"
21+
"sync"
2122

2223
"cmd/go/internal/base"
2324
"cmd/go/internal/cfg"
@@ -911,7 +912,10 @@ func WriteGoMod() {
911912
// The go.mod file has the same semantic content that it had before
912913
// (but not necessarily the same exact bytes).
913914
// Don't write go.mod, but write go.sum in case we added or trimmed sums.
914-
modfetch.WriteGoSum(keepSums(true))
915+
// 'go mod init' shouldn't write go.sum, since it will be incomplete.
916+
if cfg.CmdName != "mod init" {
917+
modfetch.WriteGoSum(keepSums(true))
918+
}
915919
return
916920
}
917921

@@ -924,7 +928,10 @@ func WriteGoMod() {
924928
index = indexModFile(new, modFile, false)
925929

926930
// Update go.sum after releasing the side lock and refreshing the index.
927-
modfetch.WriteGoSum(keepSums(true))
931+
// 'go mod init' shouldn't write go.sum, since it will be incomplete.
932+
if cfg.CmdName != "mod init" {
933+
modfetch.WriteGoSum(keepSums(true))
934+
}
928935
}()
929936

930937
// Make a best-effort attempt to acquire the side lock, only to exclude
@@ -969,41 +976,55 @@ func WriteGoMod() {
969976
// If addDirect is true, the set also includes sums for modules directly
970977
// required by go.mod, as represented by the index, with replacements applied.
971978
func keepSums(addDirect bool) map[module.Version]bool {
972-
// Walk the module graph and keep sums needed by MVS.
979+
// Re-derive the build list using the current list of direct requirements.
980+
// Keep the sum for the go.mod of each visited module version (or its
981+
// replacement).
973982
modkey := func(m module.Version) module.Version {
974983
return module.Version{Path: m.Path, Version: m.Version + "/go.mod"}
975984
}
976985
keep := make(map[module.Version]bool)
977-
replaced := make(map[module.Version]bool)
978-
reqs := Reqs()
979-
var walk func(module.Version)
980-
walk = func(m module.Version) {
981-
// If we build using a replacement module, keep the sum for the replacement,
982-
// since that's the code we'll actually use during a build.
983-
r := Replacement(m)
984-
if r.Path == "" {
985-
keep[modkey(m)] = true
986-
} else {
987-
replaced[m] = true
988-
keep[modkey(r)] = true
989-
}
990-
list, _ := reqs.Required(m)
991-
for _, r := range list {
992-
if !keep[modkey(r)] && !replaced[r] {
993-
walk(r)
986+
var mu sync.Mutex
987+
reqs := &keepSumReqs{
988+
Reqs: Reqs(),
989+
visit: func(m module.Version) {
990+
// If we build using a replacement module, keep the sum for the replacement,
991+
// since that's the code we'll actually use during a build.
992+
mu.Lock()
993+
r := Replacement(m)
994+
if r.Path == "" {
995+
keep[modkey(m)] = true
996+
} else {
997+
keep[modkey(r)] = true
994998
}
995-
}
999+
mu.Unlock()
1000+
},
1001+
}
1002+
buildList, err := mvs.BuildList(Target, reqs)
1003+
if err != nil {
1004+
panic(fmt.Sprintf("unexpected error reloading build list: %v", err))
9961005
}
997-
walk(Target)
9981006

999-
// Add entries for modules from which packages were loaded.
1007+
// Add entries for modules in the build list with paths that are prefixes of
1008+
// paths of loaded packages. We need to retain sums for modules needed to
1009+
// report ambiguous import errors. We use our re-derived build list,
1010+
// since the global build list may have been tidied.
10001011
if loaded != nil {
1001-
for _, pkg := range loaded.pkgs {
1002-
m := pkg.mod
1012+
actualMods := make(map[string]module.Version)
1013+
for _, m := range buildList[1:] {
10031014
if r := Replacement(m); r.Path != "" {
1004-
keep[r] = true
1015+
actualMods[m.Path] = r
10051016
} else {
1006-
keep[m] = true
1017+
actualMods[m.Path] = m
1018+
}
1019+
}
1020+
for _, pkg := range loaded.pkgs {
1021+
if pkg.testOf != nil || pkg.inStd {
1022+
continue
1023+
}
1024+
for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
1025+
if m, ok := actualMods[prefix]; ok {
1026+
keep[m] = true
1027+
}
10071028
}
10081029
}
10091030
}
@@ -1025,6 +1046,18 @@ func keepSums(addDirect bool) map[module.Version]bool {
10251046
return keep
10261047
}
10271048

1049+
// keepSumReqs embeds another Reqs implementation. The Required method
1050+
// calls visit for each version in the module graph.
1051+
type keepSumReqs struct {
1052+
mvs.Reqs
1053+
visit func(module.Version)
1054+
}
1055+
1056+
func (r *keepSumReqs) Required(m module.Version) ([]module.Version, error) {
1057+
r.visit(m)
1058+
return r.Reqs.Required(m)
1059+
}
1060+
10281061
func TrimGoSum() {
10291062
// Don't retain sums for direct requirements in go.mod. When TrimGoSum is
10301063
// called, go.mod has not been updated, and it may contain requirements on
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Module example.com/ambiguous/a/b is a suffix of example.com/a.
2+
This version contains no package.
3+
-- .mod --
4+
module example.com/ambiguous/a/b
5+
6+
go 1.16
7+
-- .info --
8+
{"Version":"v0.0.0-empty"}
9+
-- go.mod --
10+
module example.com/ambiguous/a/b
11+
12+
go 1.16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Module example.com/ambiguous/a is a prefix of example.com/a/b.
2+
It contains package example.com/a/b.
3+
-- .mod --
4+
module example.com/ambiguous/a
5+
6+
go 1.16
7+
8+
require example.com/ambiguous/a/b v0.0.0-empty
9+
-- .info --
10+
{"Version":"v1.0.0"}
11+
-- go.mod --
12+
module example.com/ambiguous/a
13+
14+
go 1.16
15+
16+
require example.com/ambiguous/a/b v0.0.0-empty
17+
-- b/b.go --
18+
package b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Confirm our build list.
2+
cp go.sum.buildlist-only go.sum
3+
go list -m all
4+
stdout '^example.com/ambiguous/a v1.0.0$'
5+
stdout '^example.com/ambiguous/a/b v0.0.0-empty$'
6+
7+
# If two modules could provide a package, but only one does,
8+
# 'go mod tidy' should retain sums for both zips.
9+
go mod tidy
10+
grep '^example.com/ambiguous/a v1.0.0 h1:' go.sum
11+
grep '^example.com/ambiguous/a/b v0.0.0-empty h1:' go.sum
12+
13+
# If two modules could provide a package, and we're missing a sum for one,
14+
# we should see a missing sum error, even if we have a sum for a module that
15+
# provides the package.
16+
cp go.sum.a-only go.sum
17+
! go list example.com/ambiguous/a/b
18+
stderr '^missing go.sum entry needed to verify package example.com/ambiguous/a/b is provided by exactly one module$'
19+
! go list -deps .
20+
stderr '^use.go:3:8: missing go.sum entry needed to verify package example.com/ambiguous/a/b is provided by exactly one module; try ''go mod tidy'' to add it$'
21+
22+
cp go.sum.b-only go.sum
23+
! go list example.com/ambiguous/a/b
24+
stderr '^missing go.sum entry for module providing package example.com/ambiguous/a/b$'
25+
! go list -deps .
26+
stderr '^use.go:3:8: missing go.sum entry for module providing package example.com/ambiguous/a/b; try ''go mod tidy'' to add it$'
27+
28+
-- go.mod --
29+
module m
30+
31+
go 1.15
32+
33+
require example.com/ambiguous/a v1.0.0
34+
-- go.sum.buildlist-only --
35+
example.com/ambiguous/a v1.0.0/go.mod h1:TrBl/3xTPFJ2gmMIYz53h2gkNtg0dokszEMuyS1QEb0=
36+
example.com/ambiguous/a/b v0.0.0-empty/go.mod h1:MajJq5jPEBnnXP+NTWIeXX7kwaPS1sbVEJdooTmsePQ=
37+
-- go.sum.a-only --
38+
example.com/ambiguous/a v1.0.0 h1:pGZhTXy6+titE2rNfwHwJykSjXDR4plO52PfZrBM0T8=
39+
example.com/ambiguous/a v1.0.0/go.mod h1:TrBl/3xTPFJ2gmMIYz53h2gkNtg0dokszEMuyS1QEb0=
40+
example.com/ambiguous/a/b v0.0.0-empty/go.mod h1:MajJq5jPEBnnXP+NTWIeXX7kwaPS1sbVEJdooTmsePQ=
41+
-- go.sum.b-only --
42+
example.com/ambiguous/a v1.0.0/go.mod h1:TrBl/3xTPFJ2gmMIYz53h2gkNtg0dokszEMuyS1QEb0=
43+
example.com/ambiguous/a/b v0.0.0-empty h1:xS29ReXXuhjT7jc79mo91h/PevaZ2oS9PciF1DucXtg=
44+
example.com/ambiguous/a/b v0.0.0-empty/go.mod h1:MajJq5jPEBnnXP+NTWIeXX7kwaPS1sbVEJdooTmsePQ=
45+
-- use.go --
46+
package use
47+
48+
import _ "example.com/ambiguous/a/b"

0 commit comments

Comments
 (0)