Skip to content

Commit 7e2b856

Browse files
n-peugnetottok
authored andcommitted
estimate: fix occasional infinite recursion
The "go mod graph" command returns a DAG so there can be no cycles, but modules can appear multiple times with different versions. When stripping the version from the modules we can accidentaly introduce cycles in the DAG by merging multiple versions of a module. To prevent this, we go back to saving the exact output of "go mod graph" in memory, including the version strings, and get rid of them when visiting each node. Closes: #292
1 parent a6fe7fa commit 7e2b856

File tree

1 file changed

+9
-8
lines changed

1 file changed

+9
-8
lines changed

estimate.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -277,27 +277,25 @@ func estimate(importpath, revision string) error {
277277
// imported it, separated by a single space. The module names
278278
// can have a version information delimited by the @ character
279279
src, dep, _ := strings.Cut(line, " ")
280-
// Get the module names without their version, as we do not use
281-
// this information.
282280
// The root module is the only one that does not have a version
283281
// indication with @ in the output of go mod graph. We use this
284282
// to filter out the depencencies of the "dummymod" module.
285-
dep, _, _ = strings.Cut(dep, "@")
286-
src, _, found := strings.Cut(src, "@")
287-
if !found {
283+
if !strings.Contains(src, "@") {
288284
continue
289285
}
290286
// Due to importing all packages of the estimated module in a
291287
// dummy one, some modules can depend on submodules of the
292288
// estimated one. We do as if they are dependencies of the
293289
// root one.
294-
if strings.HasPrefix(src, importpath+"/") {
290+
pattern := fmt.Sprintf("^%s[@/]", regexp.QuoteMeta(importpath))
291+
if matched, _ := regexp.MatchString(pattern, src); matched {
295292
src = importpath
296293
}
297294
// go mod graph also lists indirect dependencies as dependencies
298295
// of the current module, so we filter them out. They will still
299296
// appear later.
300-
if src == importpath && !directDeps[dep] {
297+
depMod, _, _ := strings.Cut(dep, "@")
298+
if src == importpath && !directDeps[depMod] {
301299
continue
302300
}
303301
depNode, ok := nodes[dep]
@@ -320,7 +318,10 @@ func estimate(importpath, revision string) error {
320318
needed := make(map[string]int)
321319
var visit func(n *Node, indent int)
322320
visit = func(n *Node, indent int) {
323-
mod := n.name
321+
// Get the module name without its version, as go mod graph
322+
// can return multiple times the same module with different
323+
// versions.
324+
mod, _, _ := strings.Cut(n.name, "@")
324325
count, isNeeded := needed[mod]
325326
if isNeeded {
326327
count++

0 commit comments

Comments
 (0)