cmd/go/internal/modcmd: add "vgo mod" command

The new "vgo mod" command in this CL provides a single command
for a handful of basic module maintenance and query operations,
instead of allowing them to explode the number of non-package-based
go subcommands.

In a followup CL, "vgo vendor" will become "vgo mod -vendor", and
"vgo verify" will become "vgo mod -verify".

In another followup CL, "vgo sync" will remove unused modules from go.mod.

See the help text in modcmd/mod.go for an overview of the command.

Change-Id: I114ce32f7f7c1b7725a9cab09e084c86fc99574b
Reviewed-on: https://go-review.googlesource.com/118317
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/vendor/cmd/go/internal/modcmd/mod.go b/vendor/cmd/go/internal/modcmd/mod.go
new file mode 100644
index 0000000..f8b36d4
--- /dev/null
+++ b/vendor/cmd/go/internal/modcmd/mod.go
@@ -0,0 +1,427 @@
+// 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.
+
+// Package modcmd implements the ``go mod'' command.
+package modcmd
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"strings"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/modfile"
+	"cmd/go/internal/module"
+	"cmd/go/internal/vgo"
+)
+
+var CmdMod = &base.Command{
+	UsageLine: "mod [maintenance flags]",
+	Short:     "module maintenance",
+	Long: `
+Mod performs module maintenance operations as specified by the
+following flags, which may be combined.
+
+The first group of operations provide low-level editing operations
+for manipulating go.mod from the command line or in scripts or
+other tools. They read only go.mod itself; they do not look up any
+information about the modules involved.
+
+The -init flag initializes and writes a new go.mod to the current directory,
+in effect creating a new module rooted at the current directory.
+The file go.mod must not already exist.
+If possible, mod will guess the module path from import comments
+(see 'go help importpath') or from version control configuration.
+To override this guess, use the -module flag.
+(Without -init, mod applies to the current module.)
+
+The -module flag changes (or, with -init, sets) the module's path
+(the go.mod file's module line).
+
+The -require=path@version and -droprequire=path flags
+add and drop a requirement on the given module path and version.
+Note that -require overrides any existing requirements on path.
+These flags are mainly for tools that understand the module graph.
+Users should prefer 'go get path@version' or 'go get path@none',
+which make other go.mod adjustments as needed to satisfy
+constraints imposed by other modules.
+
+The -exclude=path@version and -dropexclude=path@version flags
+add and drop an exclusion for the given module path and version.
+Note that -exclude=path@version is a no-op if that exclusion already exists.
+
+The -replace=old@v=>new@w and -dropreplace=old@v flags
+add and drop a replacement of the given module path and version pair.
+Note that -replace overrides any existing replacements for old@v.
+
+These editing flags (-require, -droprequire, -exclude, -dropexclude,
+-replace, and -dropreplace) may be repeated.
+
+The -fmt flag reformats the go.mod file without making other changes.
+This reformatting is also implied by any other modifications that use or
+rewrite the go.mod file. The only time this flag is needed is if no other
+flags are specified, as in 'go mod -fmt'.
+
+The -json flag prints the go.mod file in JSON format corresponding to these
+Go types:
+
+	type Module struct {
+		Path string
+		Version string
+	}
+
+	type GoMod struct {
+		Module Module
+		Require []Module
+		Exclude []Module
+		Replace []struct{ Old, New Module }
+	}
+
+Note that this only describes the go.mod file itself, not other modules
+referred to indirectly. For the full set of modules available to a build,
+use 'go list -m -json all'.
+
+The next group of operations provide higher-level editing and maintenance
+of a module, beyond the go.mod file.
+
+The -packages flag prints a list of packages in the module.
+It only identifies directories containing Go source code;
+it does not check that those directories contain code that builds.
+
+The -fix flag updates go.mod to use canonical version identifiers and
+to be semantically consistent. For example, consider this go.mod file:
+
+	module M
+	
+	require (
+		A v1
+		B v1.0.0
+		C v1.0.0
+		D v1.2.3
+		E dev
+	)
+	
+	exclude D v1.2.3
+
+First, -fix rewrites non-canonical version identifiers to semver form, so
+A's v1 becomes v1.0.0 and E's dev becomes the pseudo-version for the latest
+commit on the dev branch, perhaps v0.0.0-20180523231146-b3f5c0f6e5f1.
+
+Next, -fix updates requirements to respect exclusions, so the requirement
+on the excluded D v1.2.3 is updated to use the next available version of D,
+perhaps D v1.2.4 or D v1.3.0.
+
+Finally, -fix removes redundant or misleading requirements.
+For example, if A v1.0.0 itself requires B v1.2.0 and C v1.0.0,
+then go.mod's requirement of B v1.0.0 is misleading (superseded
+by B's need for v1.2.0), and its requirement of C v1.0.0 is redundant
+(implied by B's need for the same version), so both will be removed.
+
+Although -fix runs the fix-up operation in isolation, the fix-up also
+runs automatically any time a go command uses the module graph,
+to update go.mod to reflect reality. For example, the -sync, -vendor,
+and -verify flags all effectively imply -fix. And because the module
+graph defines the meaning of import statements, any commands
+that load packages—'go build', 'go test', 'go list', and so on—also
+effectively imply 'go mod -fix'.
+
+The -sync flag synchronizes go.mod with the source code in the module.
+It adds any missing modules necessary to build the current module's
+packages and dependencies, and it removes unused modules that
+don't provide any relevant packages.
+
+The -vendor flag resets the module's vendor directory to include all
+packages needed to build and test all the module's packages and
+their dependencies.
+
+The -verify flag checks that the dependencies of the current module,
+which are stored in a local downloaded source cache, have not been
+modified since being downloaded. If all the modules are unmodified,
+-verify prints "all modules verified." Otherwise it reports which
+modules have been changed and causes 'go mod' to exit with a
+non-zero status.
+	`,
+}
+
+var (
+	modFmt      = CmdMod.Flag.Bool("fmt", false, "")
+	modFix      = CmdMod.Flag.Bool("fix", false, "")
+	modJSON     = CmdMod.Flag.Bool("json", false, "")
+	modPackages = CmdMod.Flag.Bool("packages", false, "")
+	modSync     = CmdMod.Flag.Bool("sync", false, "")
+	modVendor   = CmdMod.Flag.Bool("vendor", false, "")
+	modVerify   = CmdMod.Flag.Bool("verify", false, "")
+
+	modEdits []func(*modfile.File) // edits specified in flags
+)
+
+type flagFunc func(string)
+
+func (f flagFunc) String() string     { return "" }
+func (f flagFunc) Set(s string) error { f(s); return nil }
+
+func init() {
+	CmdMod.Run = runMod // break init cycle
+
+	CmdMod.Flag.BoolVar(&vgo.CmdModInit, "init", vgo.CmdModInit, "")
+	CmdMod.Flag.StringVar(&vgo.CmdModModule, "module", vgo.CmdModModule, "")
+
+	CmdMod.Flag.Var(flagFunc(flagAddRequire), "addrequire", "")
+	CmdMod.Flag.Var(flagFunc(flagDropRequire), "droprequire", "")
+	CmdMod.Flag.Var(flagFunc(flagAddExclude), "addexclude", "")
+	CmdMod.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "")
+	CmdMod.Flag.Var(flagFunc(flagAddReplace), "addreplace", "")
+	CmdMod.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
+}
+
+func runMod(cmd *base.Command, args []string) {
+	if vgo.Init(); !vgo.Enabled() {
+		base.Fatalf("vgo mod: cannot use outside module")
+	}
+	if len(args) != 0 {
+		base.Fatalf("vgo mod: mod takes no arguments")
+	}
+
+	anyFlags :=
+		vgo.CmdModInit ||
+			vgo.CmdModModule != "" ||
+			*modVendor ||
+			*modVerify ||
+			*modJSON ||
+			*modFmt ||
+			*modFix ||
+			*modPackages ||
+			*modSync ||
+			len(modEdits) > 0
+
+	if !anyFlags {
+		base.Fatalf("vgo mod: no flags specified (see 'go help mod').")
+	}
+
+	if vgo.CmdModModule != "" {
+		if err := module.CheckPath(vgo.CmdModModule); err != nil {
+			base.Fatalf("vgo mod: invalid -module: %v", err)
+		}
+	}
+
+	if vgo.CmdModInit {
+		if _, err := os.Stat("go.mod"); err == nil {
+			base.Fatalf("vgo mod -init: go.mod already exists")
+		}
+	}
+	vgo.InitMod()
+
+	// Syntactic edits.
+
+	modFile := vgo.ModFile()
+	if vgo.CmdModModule != "" {
+		modFile.AddModuleStmt(vgo.CmdModModule)
+	}
+
+	if len(modEdits) > 0 {
+		for _, edit := range modEdits {
+			edit(modFile)
+		}
+	}
+	vgo.WriteGoMod() // write back syntactic changes
+
+	// Semantic edits.
+
+	needBuildList := *modFix
+
+	if *modSync || *modVendor || needBuildList {
+		if *modSync || *modVendor {
+			fmt.Println(vgo.ImportPaths([]string{"ALL"}))
+		} else {
+			vgo.LoadBuildList()
+		}
+		if *modSync {
+			// ImportPaths(ALL) already added missing modules.
+			// Remove unused modules.
+			panic("TODO sync unused")
+		}
+		vgo.WriteGoMod()
+		if *modVendor {
+			panic("TODO: move runVendor over")
+		}
+	}
+
+	// Read-only queries, processed only after updating go.mod.
+
+	if *modJSON {
+		modPrintJSON()
+	}
+
+	if *modPackages {
+		for _, pkg := range vgo.TargetPackages() {
+			fmt.Printf("%s\n", pkg)
+		}
+	}
+
+	if *modVerify {
+		panic("TODO: move runVerify over")
+	}
+}
+
+// parsePathVersion parses -flag=arg expecting arg to be path@version.
+func parsePathVersion(flag, arg string) (path, version string) {
+	i := strings.Index(arg, "@")
+	if i < 0 {
+		base.Fatalf("vgo mod: -%s=%s: need path@version", flag, arg)
+	}
+	path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
+	if err := module.CheckPath(path); err != nil {
+		base.Fatalf("vgo mod: -%s=%s: invalid path: %v", flag, arg, err)
+	}
+
+	// We don't call modfile.CheckPathVersion, because that insists
+	// on versions being in semver form, but here we want to allow
+	// versions like "master" or "1234abcdef", which vgo will resolve
+	// the next time it runs (or during -fix).
+	// Even so, we need to make sure the version is a valid token.
+	if modfile.MustQuote(version) {
+		base.Fatalf("vgo mod: -%s=%s: invalid version %q", flag, arg, version)
+	}
+
+	return path, version
+}
+
+// parsePath parses -flag=arg expecting arg to be path (not path@version).
+func parsePath(flag, arg string) (path string) {
+	if strings.Contains(arg, "@") {
+		base.Fatalf("vgo mod: -%s=%s: need just path, not path@version", flag, arg)
+	}
+	path = arg
+	if err := module.CheckPath(path); err != nil {
+		base.Fatalf("vgo mod: -%s=%s: invalid path: %v", flag, arg, err)
+	}
+	return path
+}
+
+// flagAddRequire implements the -addrequire flag.
+func flagAddRequire(arg string) {
+	path, version := parsePathVersion("addrequire", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.AddRequire(path, version); err != nil {
+			base.Fatalf("vgo mod: -addrequire=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagDropRequire implements the -droprequire flag.
+func flagDropRequire(arg string) {
+	path := parsePath("droprequire", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.DropRequire(path); err != nil {
+			base.Fatalf("vgo mod: -droprequire=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagAddExclude implements the -addexclude flag.
+func flagAddExclude(arg string) {
+	path, version := parsePathVersion("addexclude", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.AddExclude(path, version); err != nil {
+			base.Fatalf("vgo mod: -addexclude=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagDropExclude implements the -dropexclude flag.
+func flagDropExclude(arg string) {
+	path, version := parsePathVersion("dropexclude", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.DropExclude(path, version); err != nil {
+			base.Fatalf("vgo mod: -dropexclude=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagAddReplace implements the -addreplace flag.
+func flagAddReplace(arg string) {
+	var i int
+	if i = strings.Index(arg, "=>"); i < 0 {
+		base.Fatalf("vgo mod: -addreplace=%s: need old@v=>new[@v] (missing =>)", arg)
+	}
+	old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+2:])
+	if i = strings.Index(old, "@"); i < 0 {
+		base.Fatalf("vgo mod: -addreplace=%s: need old@v=>new[@v] (missing @ in old@v)", arg)
+	}
+	oldPath, oldVersion := strings.TrimSpace(old[:i]), strings.TrimSpace(old[i+1:])
+	if err := module.CheckPath(oldPath); err != nil {
+		base.Fatalf("vgo mod: -addreplace=%s: invalid old path: %v", arg, err)
+	}
+	if modfile.MustQuote(oldVersion) {
+		base.Fatalf("vgo mod: -addreplace=%s: invalid old version %q", arg, oldVersion)
+	}
+	var newPath, newVersion string
+	if i = strings.Index(new, "@"); i >= 0 {
+		newPath, newVersion = strings.TrimSpace(new[:i]), strings.TrimSpace(new[i+1:])
+		if err := module.CheckPath(newPath); err != nil {
+			base.Fatalf("vgo mod: -addreplace=%s: invalid new path: %v", arg, err)
+		}
+		if modfile.MustQuote(newVersion) {
+			base.Fatalf("vgo mod: -addreplace=%s: invalid new version %q", arg, newVersion)
+		}
+	} else {
+		if !modfile.IsDirectoryPath(new) {
+			base.Fatalf("vgo mod: -addreplace=%s: unversioned new path must be local directory", arg)
+		}
+		newPath = new
+	}
+
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
+			base.Fatalf("vgo mod: -addreplace=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagDropReplace implements the -dropreplace flag.
+func flagDropReplace(arg string) {
+	path, version := parsePathVersion("dropreplace", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.DropReplace(path, version); err != nil {
+			base.Fatalf("vgo mod: -dropreplace=%s: %v", arg, err)
+		}
+	})
+}
+
+// fileJSON is the -json output data structure.
+type fileJSON struct {
+	Module  module.Version
+	Require []module.Version
+	Exclude []module.Version
+	Replace []replaceJSON
+}
+
+type replaceJSON struct {
+	Old module.Version
+	New module.Version
+}
+
+// modPrintJSON prints the -json output.
+func modPrintJSON() {
+	modFile := vgo.ModFile()
+
+	var f fileJSON
+	f.Module = modFile.Module.Mod
+	for _, r := range modFile.Require {
+		f.Require = append(f.Require, r.Mod)
+	}
+	for _, x := range modFile.Exclude {
+		f.Exclude = append(f.Exclude, x.Mod)
+	}
+	for _, r := range modFile.Replace {
+		f.Replace = append(f.Replace, replaceJSON{r.Old, r.New})
+	}
+	data, err := json.MarshalIndent(&f, "", "\t")
+	if err != nil {
+		base.Fatalf("vgo mod -json: internal error: %v", err)
+	}
+	data = append(data, '\n')
+	os.Stdout.Write(data)
+}
diff --git a/vendor/cmd/go/internal/modfile/read.go b/vendor/cmd/go/internal/modfile/read.go
index f4a3926..8963fc2 100644
--- a/vendor/cmd/go/internal/modfile/read.go
+++ b/vendor/cmd/go/internal/modfile/read.go
@@ -89,6 +89,130 @@
 	return start, end
 }
 
+func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line {
+	if hint == nil {
+		// If no hint given, add to the first statement of the given type.
+	Loop:
+		for _, stmt := range x.Stmt {
+			switch stmt := stmt.(type) {
+			case *Line:
+				if stmt.Token != nil && stmt.Token[0] == tokens[0] {
+					hint = stmt
+					break Loop
+				}
+			case *LineBlock:
+				if stmt.Token[0] == tokens[0] {
+					hint = stmt
+					break Loop
+				}
+			}
+		}
+	}
+
+	if hint != nil {
+		for i, stmt := range x.Stmt {
+			switch stmt := stmt.(type) {
+			case *Line:
+				if stmt == hint {
+					// Convert line to line block.
+					stmt.InBlock = true
+					block := &LineBlock{Token: stmt.Token[:1], Line: []*Line{stmt}}
+					stmt.Token = stmt.Token[1:]
+					x.Stmt[i] = block
+					new := &Line{Token: tokens[1:], InBlock: true}
+					block.Line = append(block.Line, new)
+					return new
+				}
+			case *LineBlock:
+				if stmt == hint {
+					new := &Line{Token: tokens[1:], InBlock: true}
+					stmt.Line = append(stmt.Line, new)
+					return new
+				}
+				for j, line := range stmt.Line {
+					if line == hint {
+						// Add new line after hint.
+						stmt.Line = append(stmt.Line, nil)
+						copy(stmt.Line[j+2:], stmt.Line[j+1:])
+						new := &Line{Token: tokens[1:], InBlock: true}
+						stmt.Line[j+1] = new
+						return new
+					}
+				}
+			}
+		}
+	}
+
+	new := &Line{Token: tokens}
+	x.Stmt = append(x.Stmt, new)
+	return new
+}
+
+func (x *FileSyntax) updateLine(line *Line, tokens ...string) {
+	if line.InBlock {
+		tokens = tokens[1:]
+	}
+	line.Token = tokens
+}
+
+func (x *FileSyntax) removeLine(line *Line) {
+	line.Token = nil
+}
+
+// Cleanup cleans up the file syntax x after any edit operations.
+// To avoid quadratic behavior, removeLine marks the line as dead
+// by setting line.Token = nil but does not remove it from the slice
+// in which it appears. After edits have all been indicated,
+// calling Cleanup cleans out the dead lines.
+func (x *FileSyntax) Cleanup() {
+	w := 0
+	for _, stmt := range x.Stmt {
+		switch stmt := stmt.(type) {
+		case *Line:
+			if stmt.Token == nil {
+				continue
+			}
+		case *LineBlock:
+			ww := 0
+			for _, line := range stmt.Line {
+				if line.Token != nil {
+					stmt.Line[ww] = line
+					ww++
+				}
+			}
+			if ww == 0 {
+				continue
+			}
+			if ww == 1 {
+				// Collapse block into single line.
+				line := &Line{
+					Comments: Comments{
+						Before: commentsAdd(stmt.Before, stmt.Line[0].Before),
+						Suffix: commentsAdd(stmt.Line[0].Suffix, stmt.Suffix),
+						After:  commentsAdd(stmt.Line[0].After, stmt.After),
+					},
+					Token: stringsAdd(stmt.Token, stmt.Line[0].Token),
+				}
+				x.Stmt[w] = line
+				w++
+				continue
+			}
+			stmt.Line = stmt.Line[:ww]
+		}
+		x.Stmt[w] = stmt
+		w++
+	}
+	x.Stmt = x.Stmt[:w]
+}
+
+func commentsAdd(x, y []Comment) []Comment {
+	return append(x[:len(x):len(x)], y...)
+}
+
+func stringsAdd(x, y []string) []string {
+	return append(x[:len(x):len(x)], y...)
+}
+
 // A CommentBlock represents a top-level block of comments separate
 // from any rule.
 type CommentBlock struct {
@@ -103,9 +227,10 @@
 // A Line is a single line of tokens.
 type Line struct {
 	Comments
-	Start Position
-	Token []string
-	End   Position
+	Start   Position
+	Token   []string
+	InBlock bool
+	End     Position
 }
 
 func (x *Line) Span() (start, end Position) {
@@ -680,9 +805,10 @@
 		switch tok {
 		case '\n', _EOF, _EOL:
 			return &Line{
-				Start: start,
-				Token: token,
-				End:   end,
+				Start:   start,
+				Token:   token,
+				End:     end,
+				InBlock: true,
 			}
 		default:
 			token = append(token, sym.text)
diff --git a/vendor/cmd/go/internal/modfile/rule.go b/vendor/cmd/go/internal/modfile/rule.go
index 5a784a3..3170146 100644
--- a/vendor/cmd/go/internal/modfile/rule.go
+++ b/vendor/cmd/go/internal/modfile/rule.go
@@ -18,6 +18,7 @@
 	"cmd/go/internal/semver"
 )
 
+// A File is the parsed, interpreted form of a go.mod file.
 type File struct {
 	Module  *Module
 	Require []*Require
@@ -27,38 +28,45 @@
 	Syntax *FileSyntax
 }
 
+// A Module is the module statement.
 type Module struct {
-	Mod   module.Version
-	Major string
+	Mod    module.Version
+	Syntax *Line
 }
 
+// A Require is a single require statement.
 type Require struct {
 	Mod    module.Version
 	Syntax *Line
 }
 
+// An Exclude is a single exclude statement.
 type Exclude struct {
 	Mod    module.Version
 	Syntax *Line
 }
 
+// A Replace is a single replace statement.
 type Replace struct {
-	Old module.Version
-	New module.Version
-
+	Old    module.Version
+	New    module.Version
 	Syntax *Line
 }
 
-func (f *File) AddModuleStmt(path string) {
-	f.Module = &Module{
-		Mod: module.Version{Path: path},
-	}
+func (f *File) AddModuleStmt(path string) error {
 	if f.Syntax == nil {
 		f.Syntax = new(FileSyntax)
 	}
-	f.Syntax.Stmt = append(f.Syntax.Stmt, &Line{
-		Token: []string{"module", AutoQuote(path)},
-	})
+	if f.Module == nil {
+		f.Module = &Module{
+			Mod:    module.Version{Path: path},
+			Syntax: f.Syntax.addLine(nil, "module", AutoQuote(path)),
+		}
+	} else {
+		f.Module.Mod.Path = path
+		f.Syntax.updateLine(f.Module.Syntax, "module", AutoQuote(path))
+	}
+	return nil
 }
 
 func (f *File) AddComment(text string) {
@@ -135,7 +143,7 @@
 			fmt.Fprintf(errs, "%s:%d: repeated module statement\n", f.Syntax.Name, line.Start.Line)
 			return
 		}
-		f.Module = new(Module)
+		f.Module = &Module{Syntax: line}
 		if len(args) != 1 {
 
 			fmt.Fprintf(errs, "%s:%d: usage: module module/path [version]\n", f.Syntax.Name, line.Start.Line)
@@ -215,7 +223,7 @@
 		}
 		nv := ""
 		if len(args) == 4 {
-			if !isDirectoryPath(ns) {
+			if !IsDirectoryPath(ns) {
 				fmt.Fprintf(errs, "%s:%d: replacement module without version must be directory path (rooted or starting with ./ or ../)", f.Syntax.Name, line.Start.Line)
 				return
 			}
@@ -231,7 +239,7 @@
 				fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
 				return
 			}
-			if isDirectoryPath(ns) {
+			if IsDirectoryPath(ns) {
 				fmt.Fprintf(errs, "%s:%d: replacement module directory path %q cannot have version", f.Syntax.Name, line.Start.Line, ns)
 				return
 			}
@@ -245,7 +253,10 @@
 	}
 }
 
-func isDirectoryPath(ns string) bool {
+// IsDirectoryPath reports whether the given path should be interpreted
+// as a directory path. Just like on the go command line, relative paths
+// and rooted paths are directory paths; the rest are module paths.
+func IsDirectoryPath(ns string) bool {
 	// Because go.mod files can move from one system to another,
 	// we check all known path syntaxes, both Unix and Windows.
 	return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") ||
@@ -253,19 +264,21 @@
 		len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':'
 }
 
-func mustQuote(t string) bool {
-	for _, r := range t {
+// MustQuote reports whether s must be quoted in order to appear as
+// a single token in a go.mod line.
+func MustQuote(s string) bool {
+	for _, r := range s {
 		if !unicode.IsPrint(r) || r == ' ' || r == '"' || r == '\'' || r == '`' {
 			return true
 		}
 	}
-	return t == "" || strings.Contains(t, "//") || strings.Contains(t, "/*")
+	return s == "" || strings.Contains(s, "//") || strings.Contains(s, "/*")
 }
 
 // AutoQuote returns s or, if quoting is required for s to appear in a go.mod,
 // the quotation of s.
 func AutoQuote(s string) string {
-	if mustQuote(s) {
+	if MustQuote(s) {
 		return strconv.Quote(s)
 	}
 	return s
@@ -339,39 +352,64 @@
 	return Format(f.Syntax), nil
 }
 
-func (x *File) AddRequire(path, vers string) {
-	var syntax *Line
+// Cleanup cleans up the file f after any edit operations.
+// To avoid quadratic behavior, modifications like DropRequire
+// clear the entry but do not remove it from the slice.
+// Cleanup cleans out all the cleared entries.
+func (f *File) Cleanup() {
+	w := 0
+	for _, r := range f.Require {
+		if r.Mod.Path != "" {
+			f.Require[w] = r
+			w++
+		}
+	}
+	f.Require = f.Require[:w]
 
-	for i, stmt := range x.Syntax.Stmt {
-		switch stmt := stmt.(type) {
-		case *LineBlock:
-			if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
-				syntax = &Line{Token: []string{AutoQuote(path), vers}}
-				stmt.Line = append(stmt.Line, syntax)
-				goto End
-			}
-		case *Line:
-			if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
-				stmt.Token = stmt.Token[1:]
-				syntax = &Line{Token: []string{AutoQuote(path), vers}}
-				x.Syntax.Stmt[i] = &LineBlock{
-					Comments: stmt.Comments,
-					Token:    []string{"require"},
-					Line: []*Line{
-						stmt,
-						syntax,
-					},
-				}
-				goto End
+	w = 0
+	for _, x := range f.Exclude {
+		if x.Mod.Path != "" {
+			f.Exclude[w] = x
+			w++
+		}
+	}
+	f.Exclude = f.Exclude[:w]
+
+	w = 0
+	for _, r := range f.Replace {
+		if r.Old.Path != "" {
+			f.Replace[w] = r
+			w++
+		}
+	}
+	f.Replace = f.Replace[:w]
+
+	f.Syntax.Cleanup()
+}
+
+func (f *File) AddRequire(path, vers string) error {
+	need := true
+	for _, r := range f.Require {
+		if r.Mod.Path == path {
+			if need {
+				r.Mod.Version = vers
+				f.Syntax.updateLine(r.Syntax, "require", AutoQuote(path), vers)
+				need = false
+			} else {
+				f.Syntax.removeLine(r.Syntax)
+				*r = Require{}
 			}
 		}
 	}
 
-	syntax = &Line{Token: []string{"require", AutoQuote(path), vers}}
-	x.Syntax.Stmt = append(x.Syntax.Stmt, syntax)
+	if need {
+		f.AddNewRequire(path, vers)
+	}
+	return nil
+}
 
-End:
-	x.Require = append(x.Require, &Require{module.Version{Path: path, Version: vers}, syntax})
+func (f *File) AddNewRequire(path, vers string) {
+	f.Require = append(f.Require, &Require{module.Version{Path: path, Version: vers}, f.Syntax.addLine(nil, "require", AutoQuote(path), vers)})
 }
 
 func (f *File) SetRequire(req []module.Version) {
@@ -420,11 +458,89 @@
 	f.Syntax.Stmt = newStmts
 
 	for path, vers := range need {
-		f.AddRequire(path, vers)
+		f.AddNewRequire(path, vers)
 	}
 	f.SortBlocks()
 }
 
+func (f *File) DropRequire(path string) error {
+	for _, r := range f.Require {
+		if r.Mod.Path == path {
+			f.Syntax.removeLine(r.Syntax)
+			*r = Require{}
+		}
+	}
+	return nil
+}
+
+func (f *File) AddExclude(path, vers string) error {
+	var hint *Line
+	for _, x := range f.Exclude {
+		if x.Mod.Path == path && x.Mod.Version == vers {
+			return nil
+		}
+		if x.Mod.Path == path {
+			hint = x.Syntax
+		}
+	}
+
+	f.Exclude = append(f.Exclude, &Exclude{Mod: module.Version{Path: path, Version: vers}, Syntax: f.Syntax.addLine(hint, "exclude", AutoQuote(path), vers)})
+	return nil
+}
+
+func (f *File) DropExclude(path, vers string) error {
+	for _, x := range f.Exclude {
+		if x.Mod.Path == path && x.Mod.Version == vers {
+			f.Syntax.removeLine(x.Syntax)
+			*x = Exclude{}
+		}
+	}
+	return nil
+}
+
+func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error {
+	need := true
+	old := module.Version{Path: oldPath, Version: oldVers}
+	new := module.Version{Path: newPath, Version: newVers}
+	tokens := []string{"replace", AutoQuote(oldPath), oldVers, "=>", AutoQuote(newPath)}
+	if newVers != "" {
+		tokens = append(tokens, newVers)
+	}
+
+	var hint *Line
+	for _, r := range f.Replace {
+		if r.Old == old {
+			if need {
+				// Found replacement for old; update to use new.
+				r.New = new
+				f.Syntax.updateLine(r.Syntax, tokens...)
+				need = false
+				continue
+			}
+			// Already added; delete other replacements for same.
+			f.Syntax.removeLine(r.Syntax)
+			*r = Replace{}
+		}
+		if r.Old.Path == oldPath {
+			hint = r.Syntax
+		}
+	}
+	if need {
+		f.Replace = append(f.Replace, &Replace{Old: old, New: new, Syntax: f.Syntax.addLine(hint, tokens...)})
+	}
+	return nil
+}
+
+func (f *File) DropReplace(oldPath, oldVers string) error {
+	for _, r := range f.Replace {
+		if r.Old.Path == oldPath && r.Old.Version == oldVers {
+			f.Syntax.removeLine(r.Syntax)
+			*r = Replace{}
+		}
+	}
+	return nil
+}
+
 func (f *File) SortBlocks() {
 	f.removeDups() // otherwise sorting is unsafe
 
diff --git a/vendor/cmd/go/internal/vgo/get.go b/vendor/cmd/go/internal/vgo/get.go
index b61f7bc..988e50d 100644
--- a/vendor/cmd/go/internal/vgo/get.go
+++ b/vendor/cmd/go/internal/vgo/get.go
@@ -141,7 +141,7 @@
 			}
 		*/
 	}
-	writeGoMod()
+	WriteGoMod()
 
 	if len(args) > 0 {
 		InstallHook(args)
diff --git a/vendor/cmd/go/internal/vgo/init.go b/vendor/cmd/go/internal/vgo/init.go
index 826dfd8..1b6beb4 100644
--- a/vendor/cmd/go/internal/vgo/init.go
+++ b/vendor/cmd/go/internal/vgo/init.go
@@ -41,8 +41,29 @@
 
 	gopath string
 	srcV   string
+
+	CmdModInit   bool   // go mod -init flag
+	CmdModModule string // go mod -module flag
+
 )
 
+// TargetPackages returns the list of packages in the target (top-level) module.
+func TargetPackages() []string {
+	return matchPackages("ALL", []module.Version{Target})
+}
+
+// ModFile returns the parsed go.mod file.
+//
+// Note that after calling ImportPaths or LoadBuildList,
+// the require statements in the modfile.File are no longer
+// the source of truth and will be ignored: edits made directly
+// will be lost at the next call to WriteGoMod.
+// To make permanent changes to the require statements
+// in go.mod, edit it before calling ImportPaths or LoadBuildList.
+func ModFile() *modfile.File {
+	return modFile
+}
+
 func BinDir() string {
 	if !Enabled() {
 		panic("vgo.Bin")
@@ -110,17 +131,23 @@
 		base.Fatalf("go: %v", err)
 	}
 
-	root, _ := FindModuleRoot(cwd, "", MustBeVgo)
-	if root == "" {
-		// If invoked as vgo, insist on a mod file.
-		if MustBeVgo {
-			base.Fatalf("cannot determine module root; please create a go.mod file there")
+	if CmdModInit {
+		// Running 'go mod -init': go.mod will be created in current directory.
+		ModRoot = cwd
+	} else {
+		root, _ := FindModuleRoot(cwd, "", MustBeVgo)
+		if root == "" {
+			// If invoked as vgo, insist on a mod file.
+			if MustBeVgo {
+				base.Fatalf("cannot determine module root; please create a go.mod file there")
+			}
+			return
 		}
-		return
+		ModRoot = root
 	}
+
 	enabled = true
-	ModRoot = root
-	search.SetModRoot(root)
+	search.SetModRoot(ModRoot)
 }
 
 func Enabled() bool {
@@ -146,11 +173,21 @@
 	srcV = filepath.Join(list[0], "src/v")
 	codehost.WorkRoot = filepath.Join(srcV, "cache/vcswork")
 
+	if CmdModInit {
+		// Running go mod -init: do legacy module conversion
+		// (go.mod does not exist yet).
+		legacyModInit()
+		return
+	}
+
 	gomod := filepath.Join(ModRoot, "go.mod")
 	data, err := ioutil.ReadFile(gomod)
 	if err != nil {
-		legacyModInit()
-		return
+		if os.IsNotExist(err) {
+			legacyModInit()
+			return
+		}
+		base.Fatalf("vgo: %v", err)
 	}
 
 	f, err := modfile.Parse(gomod, data, fixVersion)
@@ -180,7 +217,7 @@
 		excluded[x.Mod] = true
 	}
 	Target = f.Module.Mod
-	writeGoMod()
+	WriteGoMod()
 }
 
 func allowed(m module.Version) bool {
@@ -282,6 +319,10 @@
 
 // Exported only for testing.
 func FindModulePath(dir string) (string, error) {
+	if CmdModModule != "" {
+		// Running go mod -init -module=x/y/z; return x/y/z.
+		return CmdModModule, nil
+	}
 	for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
 		src := filepath.Join(gpdir, "src") + string(filepath.Separator)
 		if strings.HasPrefix(dir, src) {
@@ -358,7 +399,8 @@
 	return path
 }
 
-func writeGoMod() {
+// WriteGoMod writes the current build list back to go.mod.
+func WriteGoMod() {
 	writeModHash()
 
 	if buildList != nil {
@@ -371,6 +413,7 @@
 
 	file := filepath.Join(ModRoot, "go.mod")
 	old, _ := ioutil.ReadFile(file)
+	modFile.Cleanup() // clean file after edits
 	new, err := modFile.Format()
 	if err != nil {
 		base.Fatalf("vgo: %v", err)
diff --git a/vendor/cmd/go/internal/vgo/load.go b/vendor/cmd/go/internal/vgo/load.go
index d650bf2..c56838f 100644
--- a/vendor/cmd/go/internal/vgo/load.go
+++ b/vendor/cmd/go/internal/vgo/load.go
@@ -60,7 +60,26 @@
 		ld.importList(imports, levelBuild)
 		ld.importList(testImports, levelBuild)
 	})
-	writeGoMod()
+	WriteGoMod()
+}
+
+// LoadBuildList loads the build list from go.mod.
+// The loading of the build list happens automatically in ImportPaths:
+// LoadBuildList need only be called if ImportPaths is not
+// (typically in commands that care about the module but
+// no particular package).
+func LoadBuildList() {
+	if Init(); !Enabled() {
+		base.Fatalf("vgo: LoadBuildList called but vgo not enabled")
+	}
+	InitMod()
+	iterate(func(*loader) {})
+	WriteGoMod()
+}
+
+// PkgMod returns a map from package import path to the module supplying that package.
+func PkgMod() map[string]module.Version {
+	return pkgmod
 }
 
 func ImportPaths(args []string) []string {
@@ -70,7 +89,7 @@
 	InitMod()
 
 	paths := importPaths(args)
-	writeGoMod()
+	WriteGoMod()
 	return paths
 }
 
diff --git a/vendor/cmd/go/internal/vgo/verify.go b/vendor/cmd/go/internal/vgo/verify.go
index d21c9d7..0938092 100644
--- a/vendor/cmd/go/internal/vgo/verify.go
+++ b/vendor/cmd/go/internal/vgo/verify.go
@@ -44,9 +44,7 @@
 	}
 
 	// Make go.mod consistent but don't load any packages.
-	InitMod()
-	iterate(func(*loader) {})
-	writeGoMod()
+	LoadBuildList()
 
 	ok := true
 	for _, mod := range buildList[1:] {
diff --git a/vendor/cmd/go/main.go b/vendor/cmd/go/main.go
index 0604990..d8c6b22 100644
--- a/vendor/cmd/go/main.go
+++ b/vendor/cmd/go/main.go
@@ -27,6 +27,7 @@
 	"cmd/go/internal/get"
 	"cmd/go/internal/help"
 	"cmd/go/internal/list"
+	"cmd/go/internal/modcmd"
 	"cmd/go/internal/run"
 	"cmd/go/internal/test"
 	"cmd/go/internal/tool"
@@ -49,6 +50,7 @@
 		get.CmdGet,
 		work.CmdInstall,
 		list.CmdList,
+		modcmd.CmdMod,
 		run.CmdRun,
 		test.CmdTest,
 		tool.CmdTool,
@@ -124,9 +126,16 @@
 		os.Exit(2)
 	}
 
-	vgo.Init()
-	if !vgo.MustBeVgo {
-		if vgo.Enabled() {
+	// Run vgo.Init so that each subcommand doesn't have to worry about it.
+	// Also install the vgo get command instead of the old go get command in vgo mode.
+	//
+	// If we should be vgo (if the command is named vgo or if invoked as go -vgo),
+	// and there is no go.mod file, vgo.Init will treat that as a fatal error.
+	// Normally that's fine, but if this is 'go mod -init' we need to give it a
+	// chance to create that go.mod file, so skip the init dance for 'go mod'.
+	if args[0] != "mod" {
+		vgo.Init()
+		if !vgo.MustBeVgo && vgo.Enabled() {
 			// Didn't do this above, so do it now.
 			*get.CmdGet = *vgo.CmdGet
 		}
diff --git a/vendor/cmd/go/vgo_test.go b/vendor/cmd/go/vgo_test.go
index 54ad36f..10e0d6b 100644
--- a/vendor/cmd/go/vgo_test.go
+++ b/vendor/cmd/go/vgo_test.go
@@ -5,8 +5,7 @@
 package Main_test
 
 import (
-	"cmd/go/internal/modconv"
-	"cmd/go/internal/vgo"
+	"bytes"
 	"internal/testenv"
 	"io/ioutil"
 	"os"
@@ -14,6 +13,9 @@
 	"runtime"
 	"sort"
 	"testing"
+
+	"cmd/go/internal/modconv"
+	"cmd/go/internal/vgo"
 )
 
 func TestVGOROOT(t *testing.T) {
@@ -80,6 +82,146 @@
 	}
 }
 
+func TestModEdit(t *testing.T) {
+	// Test that local replacements work
+	// and that they can use a dummy name
+	// that isn't resolvable and need not even
+	// include a dot. See golang.org/issue/24100.
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.makeTempdir()
+	tg.cd(tg.path("."))
+	tg.must(os.MkdirAll(tg.path("w"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x.go"), []byte("package x\n"), 0666))
+	tg.must(ioutil.WriteFile(tg.path("w/w.go"), []byte("package w\n"), 0666))
+
+	mustHaveGoMod := func(text string) {
+		data, err := ioutil.ReadFile(tg.path("go.mod"))
+		tg.must(err)
+		if string(data) != text {
+			t.Fatalf("go.mod mismatch:\nhave:<<<\n%s>>>\nwant:<<<\n%s\n", string(data), text)
+		}
+	}
+
+	tg.runFail("-vgo", "mod", "-init")
+	tg.grepStderr(`cannot determine module path`, "")
+	_, err := os.Stat(tg.path("go.mod"))
+	if err == nil {
+		t.Fatalf("failed go mod -init created go.mod")
+	}
+
+	tg.run("-vgo", "mod", "-init", "-module", "x.x/y/z")
+	tg.grepStderr("creating new go.mod: module x.x/y/z", "")
+	mustHaveGoMod(`module x.x/y/z
+`)
+
+	tg.runFail("-vgo", "mod", "-init")
+	mustHaveGoMod(`module x.x/y/z
+`)
+
+	tg.run("-vgo", "mod",
+		"-droprequire=x.1",
+		"-addrequire=x.1@v1.0.0",
+		"-addrequire=x.2@v1.1.0",
+		"-droprequire=x.2",
+		"-addexclude=x.1 @ v1.2.0",
+		"-addexclude=x.1@v1.2.1",
+		"-addreplace=x.1@v1.3.0=>y.1@v1.4.0",
+		"-addreplace=x.1@v1.4.0 => ../z",
+	)
+	mustHaveGoMod(`module x.x/y/z
+
+require x.1 v1.0.0
+
+exclude (
+	x.1 v1.2.0
+	x.1 v1.2.1
+)
+
+replace (
+	x.1 v1.3.0 => y.1 v1.4.0
+	x.1 v1.4.0 => ../z
+)
+`)
+
+	tg.run("-vgo", "mod",
+		"-droprequire=x.1",
+		"-dropexclude=x.1@v1.2.1",
+		"-dropreplace=x.1@v1.3.0",
+		"-addrequire=x.3@v1.99.0",
+	)
+	mustHaveGoMod(`module x.x/y/z
+
+exclude x.1 v1.2.0
+
+replace x.1 v1.4.0 => ../z
+
+require x.3 v1.99.0
+`)
+
+	tg.run("-vgo", "mod", "-json")
+	want := `{
+	"Module": {
+		"Path": "x.x/y/z",
+		"Version": ""
+	},
+	"Require": [
+		{
+			"Path": "x.3",
+			"Version": "v1.99.0"
+		}
+	],
+	"Exclude": [
+		{
+			"Path": "x.1",
+			"Version": "v1.2.0"
+		}
+	],
+	"Replace": [
+		{
+			"Old": {
+				"Path": "x.1",
+				"Version": "v1.4.0"
+			},
+			"New": {
+				"Path": "../z",
+				"Version": ""
+			}
+		}
+	]
+}
+`
+	if have := tg.getStdout(); have != want {
+		t.Fatalf("go mod -json mismatch:\nhave:<<<\n%s>>>\nwant:<<<\n%s\n", have, want)
+	}
+
+	tg.run("-vgo", "mod", "-packages")
+	want = `x.x/y/z
+x.x/y/z/w
+`
+	if have := tg.getStdout(); have != want {
+		t.Fatalf("go mod -packages mismatch:\nhave:<<<\n%s>>>\nwant:<<<\n%s\n", have, want)
+	}
+
+	data, err := ioutil.ReadFile(tg.path("go.mod"))
+	tg.must(err)
+	data = bytes.Replace(data, []byte("\n"), []byte("\r\n"), -1)
+	data = append(data, "    \n"...)
+	tg.must(ioutil.WriteFile(tg.path("go.mod"), data, 0666))
+
+	tg.run("-vgo", "mod", "-fmt")
+	mustHaveGoMod(`module x.x/y/z
+
+exclude x.1 v1.2.0
+
+replace x.1 v1.4.0 => ../z
+
+require x.3 v1.99.0
+`)
+}
+
+// TODO(rsc): Test mod -sync, mod -fix (network required).
+
 func TestLocalModule(t *testing.T) {
 	// Test that local replacements work
 	// and that they can use a dummy name