Skip to content

Commit 409434d

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/modload: scan dependencies of root paths when raising version limits in editRequirements
Fixes #47979 Change-Id: I1d9d854cda1378e20c70e6c6789b77e42e467ca7 Reviewed-on: https://go-review.googlesource.com/c/go/+/347290 Trust: Bryan C. Mills <[email protected]> Run-TryBot: Bryan C. Mills <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent 054710c commit 409434d

File tree

2 files changed

+195
-15
lines changed

2 files changed

+195
-15
lines changed

src/cmd/go/internal/modload/edit.go

+78-15
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec
192192

193193
// raiseLimitsForUpgrades increases the module versions in maxVersions to the
194194
// versions that would be needed to allow each of the modules in tryUpgrade
195-
// (individually) and all of the modules in mustSelect (simultaneously) to be
196-
// added as roots.
195+
// (individually or in any combination) and all of the modules in mustSelect
196+
// (simultaneously) to be added as roots.
197197
//
198198
// Versions not present in maxVersion are unrestricted, and it is assumed that
199199
// they will not be promoted to root requirements (and thus will not contribute
@@ -215,18 +215,42 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, p
215215
}
216216
}
217217

218-
var unprunedUpgrades []module.Version
218+
var (
219+
unprunedUpgrades []module.Version
220+
isPrunedRootPath map[string]bool
221+
)
219222
if pruning == unpruned {
220223
unprunedUpgrades = tryUpgrade
221224
} else {
225+
isPrunedRootPath = make(map[string]bool, len(maxVersion))
226+
for p := range maxVersion {
227+
isPrunedRootPath[p] = true
228+
}
222229
for _, m := range tryUpgrade {
230+
isPrunedRootPath[m.Path] = true
231+
}
232+
for _, m := range mustSelect {
233+
isPrunedRootPath[m.Path] = true
234+
}
235+
236+
allowedRoot := map[module.Version]bool{}
237+
238+
var allowRoot func(m module.Version) error
239+
allowRoot = func(m module.Version) error {
240+
if allowedRoot[m] {
241+
return nil
242+
}
243+
allowedRoot[m] = true
244+
223245
if MainModules.Contains(m.Path) {
224-
// The main module versions are already considered to be higher than any possible m, so we
225-
// won't be upgrading to it anyway and there is no point scanning its
226-
// dependencies.
227-
continue
246+
// The main module versions are already considered to be higher than any
247+
// possible m, so m cannot be selected as a root and there is no point
248+
// scanning its dependencies.
249+
return nil
228250
}
229251

252+
allow(m)
253+
230254
summary, err := goModSummary(m)
231255
if err != nil {
232256
return err
@@ -236,13 +260,27 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, p
236260
// graph, rather than loading the (potentially-overlapping) subgraph for
237261
// each upgrade individually.
238262
unprunedUpgrades = append(unprunedUpgrades, m)
239-
continue
263+
return nil
240264
}
241-
242-
allow(m)
243265
for _, r := range summary.require {
244-
allow(r)
266+
if isPrunedRootPath[r.Path] {
267+
// r could become a root as the result of an upgrade or downgrade,
268+
// in which case its dependencies will not be pruned out.
269+
// We need to allow those dependencies to be upgraded too.
270+
if err := allowRoot(r); err != nil {
271+
return err
272+
}
273+
} else {
274+
// r will not become a root, so its dependencies don't matter.
275+
// Allow only r itself.
276+
allow(r)
277+
}
245278
}
279+
return nil
280+
}
281+
282+
for _, m := range tryUpgrade {
283+
allowRoot(m)
246284
}
247285
}
248286

@@ -269,16 +307,41 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, p
269307
}
270308
}
271309

272-
if len(mustSelect) > 0 {
273-
mustGraph, err := readModGraph(ctx, pruning, mustSelect)
310+
// Explicitly allow any (transitive) upgrades implied by mustSelect.
311+
nextRoots := append([]module.Version(nil), mustSelect...)
312+
for nextRoots != nil {
313+
module.Sort(nextRoots)
314+
rs := newRequirements(pruning, nextRoots, nil)
315+
nextRoots = nil
316+
317+
rs, mustGraph, err := expandGraph(ctx, rs)
274318
if err != nil {
275319
return err
276320
}
277321

278322
for _, r := range mustGraph.BuildList() {
279-
// Some module in mustSelect requires r, so we must allow at least r.Version
280-
// unless it conflicts with an entry in mustSelect.
323+
// Some module in mustSelect requires r, so we must allow at least
324+
// r.Version (unless it conflicts with another entry in mustSelect, in
325+
// which case we will error out either way).
281326
allow(r)
327+
328+
if isPrunedRootPath[r.Path] {
329+
if v, ok := rs.rootSelected(r.Path); ok && r.Version == v {
330+
// r is already a root, so its requirements are already included in
331+
// the build list.
332+
continue
333+
}
334+
335+
// The dependencies in mustSelect may upgrade (or downgrade) an existing
336+
// root to match r, which will remain as a root. However, since r is not
337+
// a root of rs, its dependencies have been pruned out of this build
338+
// list. We need to add it back explicitly so that we allow any
339+
// transitive upgrades that r will pull in.
340+
if nextRoots == nil {
341+
nextRoots = rs.rootModules // already capped
342+
}
343+
nextRoots = append(nextRoots, r)
344+
}
282345
}
283346
}
284347

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Regression test for https://golang.org/issue/47979:
2+
#
3+
# An argument to 'go get' that results in an upgrade to a different existing
4+
# root should be allowed, and should not panic the 'go' command.
5+
6+
cp go.mod go.mod.orig
7+
8+
9+
# Transitive upgrades from upgraded roots should not prevent
10+
# 'go get -u' from performing upgrades.
11+
12+
cp go.mod.orig go.mod
13+
go get -u -d .
14+
cmp go.mod go.mod.want
15+
16+
17+
# 'go get' of a specific version should allow upgrades of
18+
# every dependency (transitively) required by that version,
19+
# including dependencies that are pulled into the module
20+
# graph by upgrading other root requirements
21+
# (in this case, example.net/indirect).
22+
23+
cp go.mod.orig go.mod
24+
go get -d example.net/[email protected]
25+
cmp go.mod go.mod.want
26+
27+
28+
-- go.mod --
29+
module golang.org/issue47979
30+
31+
go 1.17
32+
33+
replace (
34+
example.net/a v0.1.0 => ./a1
35+
example.net/a v0.2.0 => ./a2
36+
example.net/indirect v0.1.0 => ./indirect1
37+
example.net/indirect v0.2.0 => ./indirect2
38+
example.net/other v0.1.0 => ./other
39+
example.net/other v0.2.0 => ./other
40+
)
41+
42+
require (
43+
example.net/a v0.1.0
44+
example.net/other v0.1.0
45+
)
46+
47+
require example.net/indirect v0.1.0 // indirect
48+
-- go.mod.want --
49+
module golang.org/issue47979
50+
51+
go 1.17
52+
53+
replace (
54+
example.net/a v0.1.0 => ./a1
55+
example.net/a v0.2.0 => ./a2
56+
example.net/indirect v0.1.0 => ./indirect1
57+
example.net/indirect v0.2.0 => ./indirect2
58+
example.net/other v0.1.0 => ./other
59+
example.net/other v0.2.0 => ./other
60+
)
61+
62+
require (
63+
example.net/a v0.2.0
64+
example.net/other v0.2.0
65+
)
66+
67+
require example.net/indirect v0.2.0 // indirect
68+
-- issue.go --
69+
package issue
70+
71+
import _ "example.net/a"
72+
-- useother/useother.go --
73+
package useother
74+
75+
import _ "example.net/other"
76+
-- a1/go.mod --
77+
module example.net/a
78+
79+
go 1.17
80+
81+
require example.net/indirect v0.1.0
82+
-- a1/a.go --
83+
package a
84+
-- a2/go.mod --
85+
module example.net/a
86+
87+
go 1.17
88+
89+
require example.net/indirect v0.2.0
90+
-- a2/a.go --
91+
package a
92+
93+
import "example.net/indirect"
94+
-- indirect1/go.mod --
95+
module example.net/indirect
96+
97+
go 1.17
98+
99+
require example.net/other v0.1.0
100+
-- indirect1/indirect.go --
101+
package indirect
102+
-- indirect2/go.mod --
103+
module example.net/indirect
104+
105+
go 1.17
106+
107+
require example.net/other v0.2.0
108+
-- indirect2/indirect.go --
109+
package indirect
110+
111+
import "example.net/other"
112+
-- other/go.mod --
113+
module example.net/other
114+
115+
go 1.17
116+
-- other/other.go --
117+
package other

0 commit comments

Comments
 (0)