vulncheck: make Modules unique

Create Module values so that each *packages.Module corresponds to a
single *Module.

Fixes #50030.

Change-Id: Ia5e4d350454d87a7cea791e34f84e9b60e2a30c8
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/400118
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Auto-Submit: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/vulncheck/binary.go b/vulncheck/binary.go
index 273a3fd..4c5013be 100644
--- a/vulncheck/binary.go
+++ b/vulncheck/binary.go
@@ -103,19 +103,9 @@
 
 func convertModules(mods []*packages.Module) []*Module {
 	vmods := make([]*Module, len(mods))
-	// TODO(github.com/golang/go/issues/50030): should we share unique
-	// modules? Not needed now as module info is not returned by Binary.
+	convertMod := newModuleConverter()
 	for i, mod := range mods {
-		vmods[i] = &Module{
-			Path:    mod.Path,
-			Version: mod.Version,
-		}
-		if mod.Replace != nil {
-			vmods[i].Replace = &Module{
-				Path:    mod.Replace.Path,
-				Version: mod.Replace.Version,
-			}
-		}
+		vmods[i] = convertMod(mod)
 	}
 	return vmods
 }
diff --git a/vulncheck/vulncheck.go b/vulncheck/vulncheck.go
index ab6ad8b..5d927f4 100644
--- a/vulncheck/vulncheck.go
+++ b/vulncheck/vulncheck.go
@@ -51,25 +51,7 @@
 // Convert converts a slice of packages.Package to
 // a slice of corresponding vulncheck.Package.
 func Convert(pkgs []*packages.Package) []*Package {
-	ms := make(map[*packages.Module]*Module)
-	var mod func(*packages.Module) *Module
-	mod = func(m *packages.Module) *Module {
-		if m == nil {
-			return nil
-		}
-		if vm, ok := ms[m]; ok {
-			return vm
-		}
-		vm := &Module{
-			Path:    m.Path,
-			Version: m.Version,
-			Dir:     m.Dir,
-			Replace: mod(m.Replace),
-		}
-		ms[m] = vm
-		return vm
-	}
-
+	convertMod := newModuleConverter()
 	ps := make(map[*packages.Package]*Package)
 	var pkg func(*packages.Package) *Package
 	pkg = func(p *packages.Package) *Package {
@@ -84,7 +66,7 @@
 			Fset:      p.Fset,
 			Syntax:    p.Syntax,
 			TypesInfo: p.TypesInfo,
-			Module:    mod(p.Module),
+			Module:    convertMod(p.Module),
 		}
 		ps[p] = vp
 
@@ -381,3 +363,25 @@
 	}
 	return symbolVulns
 }
+
+func newModuleConverter() func(m *packages.Module) *Module {
+	pmap := map[*packages.Module]*Module{}
+	var convert func(m *packages.Module) *Module
+	convert = func(m *packages.Module) *Module {
+		if m == nil {
+			return nil
+		}
+		if vm, ok := pmap[m]; ok {
+			return vm
+		}
+		vm := &Module{
+			Path:    m.Path,
+			Version: m.Version,
+			Dir:     m.Dir,
+			Replace: convert(m.Replace),
+		}
+		pmap[m] = vm
+		return vm
+	}
+	return convert
+}