diff --git a/parser/modulepath.go b/parser/modulepath.go new file mode 100644 index 0000000..3f8e7ca --- /dev/null +++ b/parser/modulepath.go @@ -0,0 +1,82 @@ +package parser + +import ( + "bytes" + "strconv" +) + +// Content of this file was copied from the package golang.org/x/mod/modfile +// https://github.com/golang/mod/blob/v0.2.0/modfile/read.go#L877 +// Under the BSD-3-Clause licence: +// golang.org/x/mod@v0.2.0/LICENSE +/* +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +var ( + slashSlash = []byte("//") + moduleStr = []byte("module") +) + +// modulePath returns the module path from the gomod file text. +// If it cannot find a module path, it returns an empty string. +// It is tolerant of unrelated problems in the go.mod file. +func modulePath(mod []byte) string { + for len(mod) > 0 { + line := mod + mod = nil + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, mod = line[:i], line[i+1:] + } + if i := bytes.Index(line, slashSlash); i >= 0 { + line = line[:i] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, moduleStr) { + continue + } + line = line[len(moduleStr):] + n := len(line) + line = bytes.TrimSpace(line) + if len(line) == n || len(line) == 0 { + continue + } + + if line[0] == '"' || line[0] == '`' { + p, err := strconv.Unquote(string(line)) + if err != nil { + return "" // malformed quoted string or multiline module path + } + return p + } + + return string(line) + } + return "" // missing module path +} diff --git a/parser/pkgpath.go b/parser/pkgpath.go index 155d168..49f6efd 100644 --- a/parser/pkgpath.go +++ b/parser/pkgpath.go @@ -8,7 +8,6 @@ import ( "os/exec" "path" "path/filepath" - "strconv" "strings" "sync" ) @@ -91,7 +90,6 @@ func getPkgPathFromGoMod(fname string, isDir bool, goModPath string) (string, er } var ( - modulePrefix = []byte("\nmodule ") pkgPathFromGoModCache = make(map[string]string) ) @@ -109,36 +107,7 @@ func getModulePath(goModPath string) string { if err != nil { return "" } - var i int - if bytes.HasPrefix(data, modulePrefix[1:]) { - i = 0 - } else { - i = bytes.Index(data, modulePrefix) - if i < 0 { - return "" - } - i++ - } - line := data[i:] - - // Cut line at \n, drop trailing \r if present. - if j := bytes.IndexByte(line, '\n'); j >= 0 { - line = line[:j] - } - if line[len(line)-1] == '\r' { - line = line[:len(line)-1] - } - line = line[len("module "):] - - // If quoted, unquote. - pkgPath = strings.TrimSpace(string(line)) - if pkgPath != "" && pkgPath[0] == '"' { - s, err := strconv.Unquote(pkgPath) - if err != nil { - return "" - } - pkgPath = s - } + pkgPath = modulePath(data) return pkgPath } diff --git a/parser/pkgpath_test.go b/parser/pkgpath_test.go new file mode 100644 index 0000000..740f3d7 --- /dev/null +++ b/parser/pkgpath_test.go @@ -0,0 +1,39 @@ +package parser + +import "testing" + +func Test_getModulePath(t *testing.T) { + tests := map[string]struct { + goModPath string + want string + }{ + "valid go.mod without comments and deps": { + goModPath: "./testdata/default.go.mod", + want: "example.com/user/project", + }, + "valid go.mod with comments and without deps": { + goModPath: "./testdata/comments.go.mod", + want: "example.com/user/project", + }, + "valid go.mod with comments and deps": { + goModPath: "./testdata/comments_deps.go.mod", + want: "example.com/user/project", + }, + "actual easyjson go.mod": { + goModPath: "../go.mod", + want: "github.com/mailru/easyjson", + }, + "invalid go.mod with missing module": { + goModPath: "./testdata/missing_module.go", + want: "", + }, + } + for name := range tests { + tt := tests[name] + t.Run(name, func(t *testing.T) { + if got := getModulePath(tt.goModPath); got != tt.want { + t.Errorf("getModulePath() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/parser/testdata/comments.go.mod b/parser/testdata/comments.go.mod new file mode 100644 index 0000000..b42beb9 --- /dev/null +++ b/parser/testdata/comments.go.mod @@ -0,0 +1,4 @@ +// first-line comment which should bresk anything +module example.com/user/project // end-line comment which should not break anything + +go 1.13 diff --git a/parser/testdata/comments_deps.go.mod b/parser/testdata/comments_deps.go.mod new file mode 100644 index 0000000..6e2ea62 --- /dev/null +++ b/parser/testdata/comments_deps.go.mod @@ -0,0 +1,8 @@ +// first-line comment which should bresk anything +module example.com/user/project // end-line comment which should not break anything + +go 1.13 + +require ( + github.com/mailru/easyjson v0.7.0 +) diff --git a/parser/testdata/default.go.mod b/parser/testdata/default.go.mod new file mode 100644 index 0000000..77f4317 --- /dev/null +++ b/parser/testdata/default.go.mod @@ -0,0 +1,3 @@ +module example.com/user/project + +go 1.13 diff --git a/parser/testdata/missing_module.go.mod b/parser/testdata/missing_module.go.mod new file mode 100644 index 0000000..4e0332f --- /dev/null +++ b/parser/testdata/missing_module.go.mod @@ -0,0 +1,6 @@ + +go 1.13 + +require ( + github.com/mailru/easyjson v0.7.0 +)