cmd/go: make ErrNoModRoot work with local state

This change reworks the sentinel error value ErrNoModRoot and the type
noMainModulesError so that we can determine the appropriate error
message to display based on the loader state without depending on the
state directly.

This commit is part of the overall effort to eliminate global
modloader state.

Change-Id: I64de433faca96ed90fad4c153766c50575a72157
Reviewed-on: https://go-review.googlesource.com/c/go/+/711120
Reviewed-by: Michael Matloob <matloob@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Ian Alexander 2025-10-03 00:31:26 -04:00
parent 296ecc918d
commit c18fa69e52
4 changed files with 25 additions and 13 deletions

View file

@ -185,7 +185,7 @@ func (q *query) validate(loaderstate *modload.State) error {
if q.pattern == "all" {
// If there is no main module, "all" is not meaningful.
if !modload.HasModRoot(loaderstate) {
return fmt.Errorf(`cannot match "all": %v`, modload.ErrNoModRoot)
return fmt.Errorf(`cannot match "all": %v`, modload.NewNoMainModulesError(loaderstate))
}
if !versionOkForMainModule(q.version) {
// TODO(bcmills): "all@none" seems like a totally reasonable way to

View file

@ -63,7 +63,7 @@ func (e *ImportMissingError) Error() string {
}
return msg
}
if e.QueryErr != nil && e.QueryErr != ErrNoModRoot {
if e.QueryErr != nil && !errors.Is(e.QueryErr, ErrNoModRoot) {
return fmt.Sprintf("cannot find module providing package %s: %v", e.Path, e.QueryErr)
}
if cfg.BuildMod == "mod" || (cfg.BuildMod == "readonly" && allowMissingModuleImports) {
@ -484,7 +484,7 @@ func importFromModules(loaderstate *State, ctx context.Context, path string, rs
// requested package.
var queryErr error
if !HasModRoot(loaderstate) {
queryErr = ErrNoModRoot
queryErr = NewNoMainModulesError(loaderstate)
}
return module.Version{}, "", "", nil, &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd}
}

View file

@ -523,7 +523,7 @@ func Init(loaderstate *State) {
base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
}
if loaderstate.RootMode == NeedRoot {
base.Fatal(ErrNoModRoot)
base.Fatal(NewNoMainModulesError(loaderstate))
}
if !mustUseModules {
// GO111MODULE is 'auto', and we can't find a module root.
@ -538,7 +538,7 @@ func Init(loaderstate *State) {
// when it happens. See golang.org/issue/26708.
fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
if loaderstate.RootMode == NeedRoot {
base.Fatal(ErrNoModRoot)
base.Fatal(NewNoMainModulesError(loaderstate))
}
if !mustUseModules {
return
@ -560,7 +560,7 @@ func Init(loaderstate *State) {
if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil {
fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in $GOPATH %v\n", gopath)
if loaderstate.RootMode == NeedRoot {
base.Fatal(ErrNoModRoot)
base.Fatal(NewNoMainModulesError(loaderstate))
}
if !mustUseModules {
return
@ -731,21 +731,33 @@ func die(loaderstate *State) {
base.Fatalf("go: cannot find main module, but found %s in %s\n\tto create a module there, run:\n\t%sgo mod init", name, dir, cdCmd)
}
}
base.Fatal(ErrNoModRoot)
base.Fatal(NewNoMainModulesError(loaderstate))
}
var ErrNoModRoot = errors.New("no module root")
// noMainModulesError returns the appropriate error if there is no main module or
// main modules depending on whether the go command is in workspace mode.
type noMainModulesError struct{}
type noMainModulesError struct {
inWorkspaceMode bool
}
func (e noMainModulesError) Error() string {
if inWorkspaceMode(LoaderState) {
if e.inWorkspaceMode {
return "no modules were found in the current workspace; see 'go help work'"
}
return "go.mod file not found in current directory or any parent directory; see 'go help modules'"
}
var ErrNoModRoot noMainModulesError
func (e noMainModulesError) Unwrap() error {
return ErrNoModRoot
}
func NewNoMainModulesError(s *State) noMainModulesError {
return noMainModulesError{
inWorkspaceMode: inWorkspaceMode(s),
}
}
type goModDirtyError struct{}

View file

@ -146,7 +146,7 @@ func listModules(loaderstate *State, ctx context.Context, rs *Requirements, args
if arg == "all" || strings.Contains(arg, "...") {
needFullGraph = true
if !HasModRoot(loaderstate) {
base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
base.Fatalf("go: cannot match %q: %v", arg, NewNoMainModulesError(loaderstate))
}
continue
}
@ -155,7 +155,7 @@ func listModules(loaderstate *State, ctx context.Context, rs *Requirements, args
if _, ok := rs.rootSelected(loaderstate, path); !ok || rs.pruning == unpruned {
needFullGraph = true
if !HasModRoot(loaderstate) {
base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
base.Fatalf("go: cannot match %q: %v", arg, NewNoMainModulesError(loaderstate))
}
}
}
@ -164,7 +164,7 @@ func listModules(loaderstate *State, ctx context.Context, rs *Requirements, args
if _, ok := rs.rootSelected(loaderstate, arg); !ok || rs.pruning == unpruned {
needFullGraph = true
if mode&ListVersions == 0 && !HasModRoot(loaderstate) {
base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot)
base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, NewNoMainModulesError(loaderstate))
}
}
}