Skip to content

Commit 1066d59

Browse files
committed
cmd/go/internal/modload: refactor module path error
1 parent f96a66b commit 1066d59

File tree

5 files changed

+75
-61
lines changed

5 files changed

+75
-61
lines changed

src/cmd/go/internal/modload/init.go

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,13 +1075,16 @@ func CreateModFile(ctx context.Context, modPath string) {
10751075
base.Fatalf("go: %s already exists", modFilePath)
10761076
}
10771077

1078+
modPathError := modulePathError{reason: fmt.Sprintf("invalid module path %q", modPath)}
10781079
if modPath == "" {
1079-
var err error
1080-
modPath, err = findModulePath(modRoot)
1080+
inferredModPath, err := findModulePath(modRoot)
10811081
if err != nil {
10821082
base.Fatal(err)
10831083
}
1084-
} else if err := module.CheckImportPath(modPath); err != nil {
1084+
modPath = inferredModPath
1085+
modPathError.reason = fmt.Sprintf("invalid module path %q inferred from directory in GOPATH", inferredModPath)
1086+
}
1087+
if err := module.CheckImportPath(modPath); err != nil {
10851088
if pathErr, ok := err.(*module.InvalidPathError); ok {
10861089
pathErr.Kind = "module"
10871090
// Same as build.IsLocalPath()
@@ -1090,14 +1093,18 @@ func CreateModFile(ctx context.Context, modPath string) {
10901093
pathErr.Err = errors.New("is a local import path")
10911094
}
10921095
}
1093-
base.Fatal(err)
1094-
} else if _, _, ok := module.SplitPathVersion(modPath); !ok {
1096+
modPathError.message = err.Error()
1097+
base.Fatal(modPathError)
1098+
}
1099+
if _, _, ok := module.SplitPathVersion(modPath); !ok {
10951100
if strings.HasPrefix(modPath, "gopkg.in/") {
1096-
invalidMajorVersionMsg := fmt.Errorf("module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN:\n\tgo mod init %s", suggestGopkgIn(modPath))
1097-
base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
1101+
modPathError.message = "module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN"
1102+
modPathError.suggestions = []string{fmt.Sprintf("go mod init %s", suggestGopkgIn(modPath))}
1103+
} else {
1104+
modPathError.message = "major version suffixes must be in the form of /vN and are only allowed for v2 or later"
1105+
modPathError.suggestions = []string{fmt.Sprintf("go mod init %s", suggestModulePath(modPath))}
10981106
}
1099-
invalidMajorVersionMsg := fmt.Errorf("major version suffixes must be in the form of /vN and are only allowed for v2 or later:\n\tgo mod init %s", suggestModulePath(modPath))
1100-
base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
1107+
base.Fatal(modPathError)
11011108
}
11021109

11031110
fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
@@ -1140,6 +1147,36 @@ func CreateModFile(ctx context.Context, modPath string) {
11401147
}
11411148
}
11421149

1150+
// modulePathError is an error that occurs when a module path is invalid.
1151+
//
1152+
// Format:
1153+
// go: <reason>: <message>
1154+
//
1155+
// Example usage:
1156+
// <suggestions>
1157+
//
1158+
// Run 'go help mod init' for more information.
1159+
type modulePathError struct {
1160+
reason string
1161+
message string
1162+
suggestions []string
1163+
}
1164+
1165+
func (e modulePathError) Error() string {
1166+
buf := strings.Builder{}
1167+
buf.WriteString(fmt.Sprintf("%s: %s\n", e.reason, e.message))
1168+
if len(e.suggestions) > 0 {
1169+
buf.WriteString("\nExample usage:\n")
1170+
for _, suggestion := range e.suggestions {
1171+
buf.WriteString("\t")
1172+
buf.WriteString(suggestion)
1173+
buf.WriteString("\n")
1174+
}
1175+
}
1176+
buf.WriteString("\nRun 'go help mod init' for more information.\n")
1177+
return buf.String()
1178+
}
1179+
11431180
// fixVersion returns a modfile.VersionFixer implemented using the Query function.
11441181
//
11451182
// It resolves commit hashes and branch names to versions,
@@ -1698,46 +1735,23 @@ func findModulePath(dir string) (string, error) {
16981735
}
16991736

17001737
// Look for path in GOPATH.
1701-
var badPathErr error
17021738
for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
17031739
if gpdir == "" {
17041740
continue
17051741
}
17061742
if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
1707-
path := filepath.ToSlash(rel)
1708-
// gorelease will alert users publishing their modules to fix their paths.
1709-
if err := module.CheckImportPath(path); err != nil {
1710-
badPathErr = err
1711-
break
1712-
}
1713-
// Ensure the inferred path is valid.
1714-
if _, _, ok := module.SplitPathVersion(path); !ok {
1715-
if strings.HasPrefix(path, "gopkg.in/") {
1716-
badPathErr = errors.New("module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN")
1717-
} else {
1718-
badPathErr = errors.New("major version suffixes must be in the form of /vN and are only allowed for v2 or later")
1719-
}
1720-
break
1721-
}
1722-
return path, nil
1743+
return filepath.ToSlash(rel), nil
17231744
}
17241745
}
17251746

1726-
reason := "outside GOPATH, module path must be specified"
1727-
if badPathErr != nil {
1728-
// return a different error message if the module was in GOPATH, but
1729-
// the module path determined above would be an invalid path.
1730-
reason = fmt.Sprintf("bad module path inferred from directory in GOPATH: %v", badPathErr)
1747+
return "", modulePathError{
1748+
reason: "cannot determine module path for source directory",
1749+
message: "outside GOPATH, module path must be specified",
1750+
suggestions: []string{
1751+
"'go mod init example.com/m' to initialize a v0 or v1 module",
1752+
"'go mod init example.com/m/v2' to initialize a v2 module",
1753+
},
17311754
}
1732-
msg := `cannot determine module path for source directory %s (%s)
1733-
1734-
Example usage:
1735-
'go mod init example.com/m' to initialize a v0 or v1 module
1736-
'go mod init example.com/m/v2' to initialize a v2 module
1737-
1738-
Run 'go help mod init' for more information.
1739-
`
1740-
return "", fmt.Errorf(msg, dir, reason)
17411755
}
17421756

17431757
var (

src/cmd/go/testdata/script/mod_init_empty.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@ env GOPATH=$WORK/gopath
1414
# 'go mod init' should not create a go.mod file in v0 or v1 directory.
1515
cd $GOPATH/src/example.com/m/v0
1616
! go mod init
17-
stderr '(?s)^go: cannot determine module path for source directory (.*v0) \(bad module path inferred from directory in GOPATH: major version suffixes must be in the form of /vN and are only allowed for v2 or later\)(.*)'
17+
stderr '(?s)^go: invalid module path "example.com/m/v0" inferred from directory in GOPATH: major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/m/v2(.*)'
1818

1919
cd $GOPATH/src/example.com/m/v1
2020
! go mod init
21-
stderr '(?s)^go: cannot determine module path for source directory (.*v1) \(bad module path inferred from directory in GOPATH: major version suffixes must be in the form of /vN and are only allowed for v2 or later\)(.*)'
21+
stderr '(?s)^go: invalid module path "example.com/m/v1" inferred from directory in GOPATH: major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/m/v2(.*)'
2222

2323
cd $GOPATH/src/example.com/m/v2
2424
go mod init
2525
stderr '^go: creating new go.mod: module example.com/m/v2$'
2626

2727
cd $GOPATH/src/gopkg.in/m
2828
! go mod init
29-
stderr '(?s)^go: cannot determine module path for source directory (.*m) \(bad module path inferred from directory in GOPATH: module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN\)(.*)'
29+
stderr '(?s)^go: invalid module path "gopkg.in/m" inferred from directory in GOPATH: module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)'
3030

3131
-- go.mod --
3232
module example.com

src/cmd/go/testdata/script/mod_init_invalid_major.txt

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,34 @@ env GO111MODULE=on
22
env GOFLAGS=-mod=mod
33

44
! go mod init example.com/user/repo/v0
5-
stderr '(?s)^go: invalid module path "example.com/user/repo/v0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'
5+
stderr '(?s)^go: invalid module path "example.com/user/repo/v0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2(.*)'
66

77
! go mod init example.com/user/repo/v02
8-
stderr '(?s)^go: invalid module path "example.com/user/repo/v02": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'
8+
stderr '(?s)^go: invalid module path "example.com/user/repo/v02": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2(.*)'
99

1010
! go mod init example.com/user/repo/v023
11-
stderr '(?s)^go: invalid module path "example.com/user/repo/v023": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v23$'
11+
stderr '(?s)^go: invalid module path "example.com/user/repo/v023": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v23(.*)'
1212

1313
! go mod init example.com/user/repo/v1
14-
stderr '(?s)^go: invalid module path "example.com/user/repo/v1": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'
14+
stderr '(?s)^go: invalid module path "example.com/user/repo/v1": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2(.*)'
1515

1616
! go mod init example.com/user/repo/v2.0
17-
stderr '(?s)^go: invalid module path "example.com/user/repo/v2.0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'
17+
stderr '(?s)^go: invalid module path "example.com/user/repo/v2.0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2(.*)'
1818

1919
! go mod init example.com/user/repo/v2.1.4
20-
stderr '(?s)^go: invalid module path "example.com/user/repo/v2.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'
20+
stderr '(?s)^go: invalid module path "example.com/user/repo/v2.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2(.*)'
2121

2222
! go mod init example.com/user/repo/v3.5
23-
stderr '(?s)^go: invalid module path "example.com/user/repo/v3.5": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v3$'
23+
stderr '(?s)^go: invalid module path "example.com/user/repo/v3.5": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v3(.*)'
2424

2525
! go mod init example.com/user/repo/v4.1.4
26-
stderr '(?s)^go: invalid module path "example.com/user/repo/v4.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v4$'
26+
stderr '(?s)^go: invalid module path "example.com/user/repo/v4.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v4(.*)'
2727

2828
! go mod init example.com/user/repo/v.2.3
29-
stderr '(?s)^go: invalid module path "example.com/user/repo/v.2.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$'
29+
stderr '(?s)^go: invalid module path "example.com/user/repo/v.2.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2(.*)'
3030

3131
! go mod init example.com/user/repo/v.5.3
32-
stderr '(?s)^go: invalid module path "example.com/user/repo/v.5.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v5$'
32+
stderr '(?s)^go: invalid module path "example.com/user/repo/v.5.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v5(.*)'
3333

3434
! go mod init gopkg.in/pkg
3535
stderr '(?s)^go: invalid module path "gopkg.in/pkg": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/pkg.v1$'
@@ -63,20 +63,20 @@ stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v.2.3": module paths beg
6363

6464
# module paths with a trailing dot are rejected as invalid import paths
6565
! go mod init example.com/user/repo/v2.
66-
stderr '(?s)^go: malformed module path "example.com/user/repo/v2.": trailing dot in path element$'
66+
stderr '(?s)^go: invalid module path "example.com/user/repo/v2.": malformed module path "example.com/user/repo/v2.": trailing dot in path element(.*)'
6767

6868
! go mod init example.com/user/repo/v2..
69-
stderr '(?s)^go: malformed module path "example.com/user/repo/v2..": trailing dot in path element$'
69+
stderr '(?s)^go: invalid module path "example.com/user/repo/v2..": malformed module path "example.com/user/repo/v2..": trailing dot in path element(.*)'
7070

7171
! go mod init gopkg.in/user/pkg.v.2.
72-
stderr '(?s)^go: malformed module path "gopkg.in/user/pkg.v.2.": trailing dot in path element$'
72+
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v.2.": malformed module path "gopkg.in/user/pkg.v.2.": trailing dot in path element(.*)'
7373

7474
! go mod init gopkg.in/user/pkg.v.2..
75-
stderr '(?s)^go: malformed module path "gopkg.in/user/pkg.v.2..": trailing dot in path element$'
75+
stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v.2..": malformed module path "gopkg.in/user/pkg.v.2..": trailing dot in path element(.*)'
7676

7777
# module paths with spaces are also rejected
7878
! go mod init 'foo bar'
79-
stderr '(?s)^go: malformed module path "foo bar": invalid char '' ''$'
79+
stderr '(?s)^go: invalid module path "foo bar": malformed module path "foo bar": invalid char '' ''(.*)'
8080

8181
! go mod init 'foo bar baz'
82-
stderr '(?s)^go: malformed module path "foo bar baz": invalid char '' ''$'
82+
stderr '(?s)^go: invalid module path "foo bar baz": malformed module path "foo bar baz": invalid char '' ''(.*)'

src/cmd/go/testdata/script/mod_init_path.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
env GO111MODULE=on
22

33
! go mod init .
4-
stderr '^go: malformed module path ".": is a local import path$'
4+
stderr '^go: invalid module path ".": malformed module path ".": is a local import path(.*)'
55

66
cd x
77
go mod init example.com/x

src/cmd/go/testdata/script/mod_invalid_path.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ stderr '^go: error reading go.mod: missing module declaration. To specify the mo
1212
# but are a valid Windows file name.
1313
cd $WORK/'gopath/src/m''d'
1414
! go mod init
15-
stderr 'cannot determine module path'
15+
stderr '(?s)^go: invalid module path "m''d" inferred from directory in GOPATH: malformed module path "m''d": invalid char ''\\''''(.*)'
1616

1717
# Test that a go.mod file is rejected when its module declaration has a path that can't
1818
# possibly be a module path, because it isn't even a valid import path

0 commit comments

Comments
 (0)