cmd/go: add modload.NeedRoot mode for commands that need module root

This makes error reporting a bit more consistent for 'go mod'
subcommands. Most of these commands only work in module mode when a
go.mod file is present.

Setting modload.ForceUseModules reports an error when GO111MODULE=off.

Setting modload.RootMode to modload.NeedRoot reports an error when no
go.mod file is present.

Change-Id: I1daa8d2971cb8658e0c804765839d903734a412e
Reviewed-on: https://go-review.googlesource.com/c/go/+/254369
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go
index 6227fd9..050a2e0 100644
--- a/src/cmd/go/internal/modcmd/download.go
+++ b/src/cmd/go/internal/modcmd/download.go
@@ -80,9 +80,7 @@
 
 func runDownload(ctx context.Context, cmd *base.Command, args []string) {
 	// Check whether modules are enabled and whether we're in a module.
-	if cfg.Getenv("GO111MODULE") == "off" {
-		base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
-	}
+	modload.ForceUseModules = true
 	if !modload.HasModRoot() && len(args) == 0 {
 		base.Fatalf("go mod download: no modules specified (see 'go help mod download')")
 	}
diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go
index a149b65..3277548 100644
--- a/src/cmd/go/internal/modcmd/graph.go
+++ b/src/cmd/go/internal/modcmd/graph.go
@@ -13,7 +13,6 @@
 	"sort"
 
 	"cmd/go/internal/base"
-	"cmd/go/internal/cfg"
 	"cmd/go/internal/modload"
 
 	"golang.org/x/mod/module"
@@ -39,14 +38,8 @@
 	if len(args) > 0 {
 		base.Fatalf("go mod graph: graph takes no arguments")
 	}
-	// Checks go mod expected behavior
-	if !modload.Enabled() {
-		if cfg.Getenv("GO111MODULE") == "off" {
-			base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
-		} else {
-			base.Fatalf("go: cannot find main module; see 'go help modules'")
-		}
-	}
+	modload.ForceUseModules = true
+	modload.RootMode = modload.NeedRoot
 	modload.LoadAllModules(ctx)
 
 	reqs := modload.MinReqs()
diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go
index 21b2356..7cfc0e6 100644
--- a/src/cmd/go/internal/modcmd/init.go
+++ b/src/cmd/go/internal/modcmd/init.go
@@ -40,9 +40,7 @@
 	if len(args) == 1 {
 		modload.CmdModModule = args[0]
 	}
-	if os.Getenv("GO111MODULE") == "off" {
-		base.Fatalf("go mod init: modules disabled by GO111MODULE=off; see 'go help modules'")
-	}
+	modload.ForceUseModules = true
 	modFilePath := modload.ModFilePath()
 	if _, err := os.Stat(modFilePath); err == nil {
 		base.Fatalf("go mod init: go.mod already exists")
diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go
index 30df674..cbe3ded 100644
--- a/src/cmd/go/internal/modcmd/tidy.go
+++ b/src/cmd/go/internal/modcmd/tidy.go
@@ -50,6 +50,8 @@
 	// that are in 'all' but outside of the main module, we must explicitly
 	// request that their test dependencies be included.
 	modload.LoadTests = true
+	modload.ForceUseModules = true
+	modload.RootMode = modload.NeedRoot
 
 	modload.LoadALL(ctx)
 	modload.TidyBuildList()
diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go
index 91d2509..44094b7 100644
--- a/src/cmd/go/internal/modcmd/vendor.go
+++ b/src/cmd/go/internal/modcmd/vendor.go
@@ -47,6 +47,8 @@
 	if len(args) != 0 {
 		base.Fatalf("go mod vendor: vendor takes no arguments")
 	}
+	modload.ForceUseModules = true
+	modload.RootMode = modload.NeedRoot
 	pkgs := modload.LoadVendor(ctx)
 
 	vdir := filepath.Join(modload.ModRoot(), "vendor")
diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go
index 7700588..bd591d3 100644
--- a/src/cmd/go/internal/modcmd/verify.go
+++ b/src/cmd/go/internal/modcmd/verify.go
@@ -14,7 +14,6 @@
 	"runtime"
 
 	"cmd/go/internal/base"
-	"cmd/go/internal/cfg"
 	"cmd/go/internal/modfetch"
 	"cmd/go/internal/modload"
 
@@ -45,14 +44,8 @@
 		// NOTE(rsc): Could take a module pattern.
 		base.Fatalf("go mod verify: verify takes no arguments")
 	}
-	// Checks go mod expected behavior
-	if !modload.Enabled() || !modload.HasModRoot() {
-		if cfg.Getenv("GO111MODULE") == "off" {
-			base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
-		} else {
-			base.Fatalf("go: cannot find main module; see 'go help modules'")
-		}
-	}
+	modload.ForceUseModules = true
+	modload.RootMode = modload.NeedRoot
 
 	// Only verify up to GOMAXPROCS zips at once.
 	type token struct{}
diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go
index 8454fdf..ea7c28e 100644
--- a/src/cmd/go/internal/modcmd/why.go
+++ b/src/cmd/go/internal/modcmd/why.go
@@ -61,6 +61,8 @@
 }
 
 func runWhy(ctx context.Context, cmd *base.Command, args []string) {
+	modload.ForceUseModules = true
+	modload.RootMode = modload.NeedRoot
 	loadALL := modload.LoadALL
 	if *whyVendor {
 		loadALL = modload.LoadVendor
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index 2f0f60b..f93abee 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -77,8 +77,9 @@
 	// file the current directory or in parent directories.
 	NoRoot
 
-	// TODO(jayconrod): add NeedRoot for commands like 'go mod vendor' that
-	// don't make sense without a main module.
+	// NeedRoot is used for commands that must run in module mode and don't
+	// make sense without a main module.
+	NeedRoot
 )
 
 // ModFile returns the parsed go.mod file.
@@ -172,6 +173,9 @@
 			if cfg.ModFile != "" {
 				base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
 			}
+			if RootMode == NeedRoot {
+				base.Fatalf("go: cannot find main module; see 'go help modules'")
+			}
 			if !mustUseModules {
 				// GO111MODULE is 'auto', and we can't find a module root.
 				// Stay in GOPATH mode.
diff --git a/src/cmd/go/testdata/script/mod_off.txt b/src/cmd/go/testdata/script/mod_off.txt
index cada6de..a73a58d 100644
--- a/src/cmd/go/testdata/script/mod_off.txt
+++ b/src/cmd/go/testdata/script/mod_off.txt
@@ -4,7 +4,7 @@
 # GO111MODULE=off when outside of GOPATH will fatal
 # with an error message, even with some source code in the directory and a go.mod.
 ! go mod init
-stderr 'go mod init: modules disabled by GO111MODULE=off; see ''go help modules'''
+stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules'''
 ! go mod graph
 stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules'''
 ! go mod verify
@@ -16,7 +16,7 @@
 mkdir z
 cd z
 ! go mod init
-stderr 'go mod init: modules disabled by GO111MODULE=off; see ''go help modules'''
+stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules'''
 ! go mod graph
 stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules'''
 ! go mod verify
diff --git a/src/cmd/go/testdata/script/mod_off_init.txt b/src/cmd/go/testdata/script/mod_off_init.txt
index 1339c8a..2aec0b3 100644
--- a/src/cmd/go/testdata/script/mod_off_init.txt
+++ b/src/cmd/go/testdata/script/mod_off_init.txt
@@ -2,4 +2,4 @@
 # ignored anyway due to GO111MODULE=off.
 env GO111MODULE=off
 ! go mod init
-stderr 'go mod init: modules disabled by GO111MODULE=off; see ''go help modules'''
+stderr 'go: modules disabled by GO111MODULE=off; see ''go help modules'''