Follow-up to CL 297530.
For #43985
For #34992
Change-Id: I2cfa6c41c013e627c3464c383ca42f5c9ebe521a
Reviewed-on: https://go-review.googlesource.com/c/go/+/297634
Trust: Jay Conrod <jayconrod@google.com>
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2
golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
- golang.org/x/mod v0.4.2-0.20210309222212-d6ab96f2441f
+ golang.org/x/mod v0.4.3-0.20210310185834-19d50cac98aa
golang.org/x/sys v0.0.0-20210218145245-beda7e5e158e // indirect
golang.org/x/tools v0.1.1-0.20210220032852-2363391a5b2f
)
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2-0.20210309222212-d6ab96f2441f h1:mQozKYYFIVK0MXcDB8Dvw0dR3rxKLnkSCJHWznfaodQ=
-golang.org/x/mod v0.4.2-0.20210309222212-d6ab96f2441f/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.3-0.20210310185834-19d50cac98aa h1:++oSKjoJSsXNHyhUdK1BtBKMAaMHER+GWyKN3319OZA=
+golang.org/x/mod v0.4.3-0.20210310185834-19d50cac98aa/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
// modulePrefixesExcludingTarget returns all prefixes of path that may plausibly
// exist as a module, excluding targetPrefix but otherwise including path
-// itself, sorted by descending length.
+// itself, sorted by descending length. Prefixes that are not valid module paths
+// but are valid package paths (like "m" or "example.com/.gen") are included,
+// since they might be replaced.
func modulePrefixesExcludingTarget(path string) []string {
prefixes := make([]string, 0, strings.Count(path, "/")+1)
noPackage *PackageNotInModuleError
noVersion *NoMatchingVersionError
noPatchBase *NoPatchBaseError
+ invalidPath *module.InvalidPathError // see comment in case below
notExistErr error
)
for _, r := range results {
if noPatchBase == nil {
noPatchBase = rErr
}
+ case *module.InvalidPathError:
+ // The prefix was not a valid module path, and there was no replacement.
+ // Prefixes like this may appear in candidateModules, since we handle
+ // replaced modules that weren't required in the repo lookup process
+ // (see lookupRepo).
+ //
+ // A shorter prefix may be a valid module path and may contain a valid
+ // import path, so this is a low-priority error.
+ if invalidPath == nil {
+ invalidPath = rErr
+ }
default:
if errors.Is(rErr, fs.ErrNotExist) {
if notExistErr == nil {
err = noVersion
case noPatchBase != nil:
err = noPatchBase
+ case invalidPath != nil:
+ err = invalidPath
case notExistErr != nil:
err = notExistErr
default:
var buf bytes.Buffer
z := zip.NewWriter(&buf)
for _, f := range a.Files {
- if strings.HasPrefix(f.Name, ".") {
+ if f.Name == ".info" || f.Name == ".mod" || f.Name == ".zip" {
continue
}
var zipName string
--- /dev/null
+-- .info --
+{"Version":"v1.0.0"}
+-- .mod --
+module example.com/dotname
+
+go 1.16
+-- go.mod --
+module example.com/dotname
+
+go 1.16
+-- .dot/dot.go --
+package dot
--- /dev/null
+# Test that an import path containing an element with a leading dot
+# in another module is valid.
+
+# 'go get' works with no version query.
+cp go.mod.empty go.mod
+go get -d example.com/dotname/.dot
+go list -m example.com/dotname
+stdout '^example.com/dotname v1.0.0$'
+
+# 'go get' works with a version query.
+cp go.mod.empty go.mod
+go get -d example.com/dotname/.dot@latest
+go list -m example.com/dotname
+stdout '^example.com/dotname v1.0.0$'
+
+# 'go get' works on an importing package.
+cp go.mod.empty go.mod
+go get -d .
+go list -m example.com/dotname
+stdout '^example.com/dotname v1.0.0$'
+
+# 'go list' works on the dotted package.
+go list example.com/dotname/.dot
+stdout '^example.com/dotname/.dot$'
+
+# 'go list' works on an importing package.
+go list .
+stdout '^m$'
+
+# 'go mod tidy' works.
+cp go.mod.empty go.mod
+go mod tidy
+go list -m example.com/dotname
+stdout '^example.com/dotname v1.0.0$'
+
+-- go.mod.empty --
+module m
+
+go 1.16
+-- go.sum --
+example.com/dotname v1.0.0 h1:Q0JMAn464CnwFVCshs1n4+f5EFiW/eRhnx/fTWjw2Ag=
+example.com/dotname v1.0.0/go.mod h1:7K4VLT7QylRI8H7yZwUkeDH2s19wQnyfp/3oBlItWJ0=
+-- use.go --
+package use
+
+import _ "example.com/dotname/.dot"
# The '+' character should be disallowed in module paths, but allowed in package
# paths within valid modules.
+# 'go list' accepts package paths with pluses.
+cp go.mod.orig go.mod
go get -d example.net/cmd
go list example.net/cmd/x++
+# 'go list -m' rejects module paths with pluses.
! go list -versions -m 'example.net/bad++'
stderr '^go list -m: malformed module path "example.net/bad\+\+": invalid char ''\+''$'
-# TODO(bcmills): 'go get -d example.net/cmd/x++' should also work, but currently
-# it does not. This might be fixed by https://golang.org/cl/297891.
-! go get -d example.net/cmd/x++
-stderr '^go get: malformed module path "example.net/cmd/x\+\+": invalid char ''\+''$'
+# 'go get' accepts package paths with pluses.
+cp go.mod.orig go.mod
+go get -d example.net/cmd/x++
+go list -m example.net/cmd
+stdout '^example.net/cmd v0.0.0-00010101000000-000000000000 => ./cmd$'
--- go.mod --
+-- go.mod.orig --
module example.com/m
go 1.16
func (e *InvalidVersionError) Unwrap() error { return e.Err }
+// An InvalidPathError indicates a module, import, or file path doesn't
+// satisfy all naming constraints. See CheckPath, CheckImportPath,
+// and CheckFilePath for specific restrictions.
+type InvalidPathError struct {
+ Kind string // "module", "import", or "file"
+ Path string
+ Err error
+}
+
+func (e *InvalidPathError) Error() string {
+ return fmt.Sprintf("malformed %s path %q: %v", e.Kind, e.Path, e.Err)
+}
+
+func (e *InvalidPathError) Unwrap() error { return e.Err }
+
// Check checks that a given module path, version pair is valid.
// In addition to the path being a valid module path
// and the version being a valid semantic version,
// this second requirement is replaced by a requirement that the path
// follow the gopkg.in server's conventions.
// Third, no path element may begin with a dot.
-func CheckPath(path string) error {
+func CheckPath(path string) (err error) {
+ defer func() {
+ if err != nil {
+ err = &InvalidPathError{Kind: "module", Path: path, Err: err}
+ }
+ }()
+
if err := checkPath(path, modulePath); err != nil {
- return fmt.Errorf("malformed module path %q: %v", path, err)
+ return err
}
i := strings.Index(path, "/")
if i < 0 {
i = len(path)
}
if i == 0 {
- return fmt.Errorf("malformed module path %q: leading slash", path)
+ return fmt.Errorf("leading slash")
}
if !strings.Contains(path[:i], ".") {
- return fmt.Errorf("malformed module path %q: missing dot in first path element", path)
+ return fmt.Errorf("missing dot in first path element")
}
if path[0] == '-' {
- return fmt.Errorf("malformed module path %q: leading dash in first path element", path)
+ return fmt.Errorf("leading dash in first path element")
}
for _, r := range path[:i] {
if !firstPathOK(r) {
- return fmt.Errorf("malformed module path %q: invalid char %q in first path element", path, r)
+ return fmt.Errorf("invalid char %q in first path element", r)
}
}
if _, _, ok := SplitPathVersion(path); !ok {
- return fmt.Errorf("malformed module path %q: invalid version", path)
+ return fmt.Errorf("invalid version")
}
return nil
}
// subtleties of Unicode.
func CheckImportPath(path string) error {
if err := checkPath(path, importPath); err != nil {
- return fmt.Errorf("malformed import path %q: %v", path, err)
+ return &InvalidPathError{Kind: "import", Path: path, Err: err}
}
return nil
}
filePath
)
-// checkPath checks that a general path is valid.
-// It returns an error describing why but not mentioning path.
-// Because these checks apply to both module paths and import paths,
-// the caller is expected to add the "malformed ___ path %q: " prefix.
-// fileName indicates whether the final element of the path is a file name
-// (as opposed to a directory name).
+// checkPath checks that a general path is valid. kind indicates what
+// specific constraints should be applied.
+//
+// checkPath returns an error describing why the path is not valid.
+// Because these checks apply to module, import, and file paths,
+// and because other checks may be applied, the caller is expected to wrap
+// this error with InvalidPathError.
func checkPath(path string, kind pathKind) error {
if !utf8.ValidString(path) {
return fmt.Errorf("invalid UTF-8")
// subtleties of Unicode.
func CheckFilePath(path string) error {
if err := checkPath(path, filePath); err != nil {
- return fmt.Errorf("malformed file path %q: %v", path, err)
+ return &InvalidPathError{Kind: "file", Path: path, Err: err}
}
return nil
}
golang.org/x/crypto/ed25519
golang.org/x/crypto/ed25519/internal/edwards25519
golang.org/x/crypto/ssh/terminal
-# golang.org/x/mod v0.4.2-0.20210309222212-d6ab96f2441f
+# golang.org/x/mod v0.4.3-0.20210310185834-19d50cac98aa
## explicit
golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/modfile