x/vgo: Fix parsing of go.mod with packages from gopkg.in

This addresses an issue where Parsing of `go.mod` with modules from `gopkg.in` fails. The `go.mod` file is produced automatically, so the first time something like `vgo build` is run (before `go.mod` is created), it succeeds, but the second time it is run, it fails.

This fixes the latest issues described in https://github.com/golang/go/issues/24099

The funcs `parseGopkgIn` and `dotV` were pulled in (and slightly modified/simplified) from `vendor/cmd/go/internal/modfetch/gopkgin.go`.

I'm sure that this is not the best way to handle the special case of `gopkg.in` (especially compared to the effort put into creating the `VersionFixer`), but it's a starting point that has test cases that prove the autogenerated `go.mod` file can't be parsed.

Fixes #24099

Change-Id: Ie0bee2a50b330a9f85c1459ff6eb0bfa43058bf1
GitHub-Last-Rev: f60854418215026cdc3c9292a99e6dfd90557cd2
GitHub-Pull-Request: golang/vgo#4
Reviewed-on: https://go-review.googlesource.com/112277
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/vendor/cmd/go/internal/modfetch/gopkgin.go b/vendor/cmd/go/internal/modfetch/gopkgin.go
index ec72dcd..d49b60b 100644
--- a/vendor/cmd/go/internal/modfetch/gopkgin.go
+++ b/vendor/cmd/go/internal/modfetch/gopkgin.go
@@ -9,49 +9,12 @@
 import (
 	"cmd/go/internal/modfetch/codehost"
 	"cmd/go/internal/modfetch/gitrepo"
+	"cmd/go/internal/modfile"
 	"fmt"
-	"strings"
 )
 
-func parseGopkgIn(path string) (root, repo, major, subdir string, ok bool) {
-	if !strings.HasPrefix(path, "gopkg.in/") {
-		return
-	}
-	f := strings.Split(path, "/")
-	if len(f) >= 2 {
-		if elem, v, ok := dotV(f[1]); ok {
-			root = strings.Join(f[:2], "/")
-			repo = "github.com/go-" + elem + "/" + elem
-			major = v
-			subdir = strings.Join(f[2:], "/")
-			return root, repo, major, subdir, true
-		}
-	}
-	if len(f) >= 3 {
-		if elem, v, ok := dotV(f[2]); ok {
-			root = strings.Join(f[:3], "/")
-			repo = "github.com/" + f[1] + "/" + elem
-			major = v
-			subdir = strings.Join(f[3:], "/")
-			return root, repo, major, subdir, true
-		}
-	}
-	return
-}
-
-func dotV(name string) (elem, v string, ok bool) {
-	i := len(name) - 1
-	for i >= 0 && '0' <= name[i] && name[i] <= '9' {
-		i--
-	}
-	if i <= 2 || i+1 >= len(name) || name[i-1] != '.' || name[i] != 'v' || name[i+1] == '0' && len(name) != i+2 {
-		return "", "", false
-	}
-	return name[:i-1], name[i:], true
-}
-
 func gopkginLookup(path string) (codehost.Repo, error) {
-	root, _, _, _, ok := parseGopkgIn(path)
+	root, _, _, _, ok := modfile.ParseGopkgIn(path)
 	if !ok {
 		return nil, fmt.Errorf("invalid gopkg.in/ path: %q", path)
 	}
diff --git a/vendor/cmd/go/internal/modfile/gopkgin.go b/vendor/cmd/go/internal/modfile/gopkgin.go
new file mode 100644
index 0000000..c94b384
--- /dev/null
+++ b/vendor/cmd/go/internal/modfile/gopkgin.go
@@ -0,0 +1,47 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TODO: Figure out what gopkg.in should do.
+
+package modfile
+
+import "strings"
+
+// ParseGopkgIn splits gopkg.in import paths into their constituent parts
+func ParseGopkgIn(path string) (root, repo, major, subdir string, ok bool) {
+	if !strings.HasPrefix(path, "gopkg.in/") {
+		return
+	}
+	f := strings.Split(path, "/")
+	if len(f) >= 2 {
+		if elem, v, ok := dotV(f[1]); ok {
+			root = strings.Join(f[:2], "/")
+			repo = "github.com/go-" + elem + "/" + elem
+			major = v
+			subdir = strings.Join(f[2:], "/")
+			return root, repo, major, subdir, true
+		}
+	}
+	if len(f) >= 3 {
+		if elem, v, ok := dotV(f[2]); ok {
+			root = strings.Join(f[:3], "/")
+			repo = "github.com/" + f[1] + "/" + elem
+			major = v
+			subdir = strings.Join(f[3:], "/")
+			return root, repo, major, subdir, true
+		}
+	}
+	return
+}
+
+func dotV(name string) (elem, v string, ok bool) {
+	i := len(name) - 1
+	for i >= 0 && '0' <= name[i] && name[i] <= '9' {
+		i--
+	}
+	if i <= 2 || i+1 >= len(name) || name[i-1] != '.' || name[i] != 'v' || name[i+1] == '0' && len(name) != i+2 {
+		return "", "", false
+	}
+	return name[:i-1], name[i:], true
+}
diff --git a/vendor/cmd/go/internal/modfile/read_test.go b/vendor/cmd/go/internal/modfile/read_test.go
index 3c73528..2c617b8 100644
--- a/vendor/cmd/go/internal/modfile/read_test.go
+++ b/vendor/cmd/go/internal/modfile/read_test.go
@@ -101,8 +101,11 @@
 		}
 
 		pf1, err := Parse(base, data, nil)
-		if err != nil && base == "testdata/replace2.in" {
-			t.Errorf("should parse %v: %v", base, err)
+		if err != nil {
+			switch base {
+			case "testdata/replace2.in", "testdata/gopkg.in.golden":
+				t.Errorf("should parse %v: %v", base, err)
+			}
 		}
 		if err == nil {
 			pf2, err := Parse(base, ndata, nil)
diff --git a/vendor/cmd/go/internal/modfile/rule.go b/vendor/cmd/go/internal/modfile/rule.go
index 11fa36c..bf62d37 100644
--- a/vendor/cmd/go/internal/modfile/rule.go
+++ b/vendor/cmd/go/internal/modfile/rule.go
@@ -293,6 +293,10 @@
 }
 
 func moduleMajorVersion(p string) (string, error) {
+	if _, _, major, _, ok := ParseGopkgIn(p); ok {
+		return major, nil
+	}
+
 	start := 0
 	for i := 0; i < len(p); i++ {
 		if p[i] == '/' {
diff --git a/vendor/cmd/go/internal/modfile/testdata/gopkg.in.golden b/vendor/cmd/go/internal/modfile/testdata/gopkg.in.golden
new file mode 100644
index 0000000..41669b3
--- /dev/null
+++ b/vendor/cmd/go/internal/modfile/testdata/gopkg.in.golden
@@ -0,0 +1,6 @@
+module x
+
+require (
+	gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528
+	gopkg.in/yaml.v2 v2.2.1
+)