From b2f2446b46bf02821d90ebedadae2bf7ae0e880e Mon Sep 17 00:00:00 2001 From: Taras Madan Date: Mon, 5 Sep 2022 14:27:54 +0200 Subject: go.mod, vendor: update (#3358) * go.mod, vendor: remove unnecessary dependencies Commands: 1. go mod tidy 2. go mod vendor * go.mod, vendor: update cloud.google.com/go Commands: 1. go get -u cloud.google.com/go 2. go mod tidy 3. go mod vendor * go.mod, vendor: update cloud.google.com/* Commands: 1. go get -u cloud.google.com/storage cloud.google.com/logging 2. go mod tidy 3. go mod vendor * go.mod, .golangci.yml, vendor: update *lint* Commands: 1. go get -u golang.org/x/tools github.com/golangci/golangci-lint@v1.47.0 2. go mod tidy 3. go mod vendor 4. edit .golangci.yml to suppress new errors (resolved in the same PR later) * all: fix lint errors hash.go: copy() recommended by gosimple parse.go: ent is never nil verifier.go: signal.Notify() with unbuffered channel is bad. Have no idea why. * .golangci.yml: adjust godot rules check-all is deprecated, but still work if you're hesitating too - I'll remove this commit --- vendor/github.com/mgechev/dots/.travis.yml | 2 - vendor/github.com/mgechev/dots/LICENSE | 21 - vendor/github.com/mgechev/dots/README.md | 100 ----- vendor/github.com/mgechev/dots/resolve.go | 456 --------------------- vendor/github.com/mgechev/revive/config/config.go | 84 +++- .../mgechev/revive/formatter/checkstyle.go | 9 +- .../github.com/mgechev/revive/formatter/default.go | 4 +- .../mgechev/revive/formatter/friendly.go | 22 +- vendor/github.com/mgechev/revive/formatter/json.go | 4 +- .../github.com/mgechev/revive/formatter/ndjson.go | 4 +- .../github.com/mgechev/revive/formatter/plain.go | 4 +- .../github.com/mgechev/revive/formatter/sarif.go | 107 +++++ .../github.com/mgechev/revive/formatter/stylish.go | 8 +- vendor/github.com/mgechev/revive/formatter/unix.go | 4 +- .../revive/internal/typeparams/typeparams.go | 29 ++ .../revive/internal/typeparams/typeparams_go117.go | 12 + .../revive/internal/typeparams/typeparams_go118.go | 20 + vendor/github.com/mgechev/revive/lint/config.go | 3 + vendor/github.com/mgechev/revive/lint/file.go | 29 +- vendor/github.com/mgechev/revive/lint/linter.go | 78 +++- vendor/github.com/mgechev/revive/lint/package.go | 84 ++-- vendor/github.com/mgechev/revive/lint/rule.go | 4 +- .../github.com/mgechev/revive/rule/add-constant.go | 110 ++--- .../mgechev/revive/rule/argument-limit.go | 40 +- vendor/github.com/mgechev/revive/rule/atomic.go | 6 +- .../mgechev/revive/rule/banned-characters.go | 90 ++++ .../github.com/mgechev/revive/rule/bare-return.go | 6 +- .../mgechev/revive/rule/blank-imports.go | 83 ++-- .../mgechev/revive/rule/bool-literal-in-expr.go | 6 +- .../github.com/mgechev/revive/rule/call-to-gc.go | 8 +- .../mgechev/revive/rule/cognitive-complexity.go | 39 +- .../mgechev/revive/rule/confusing-naming.go | 9 +- .../mgechev/revive/rule/confusing-results.go | 5 +- .../mgechev/revive/rule/constant-logical-expr.go | 20 +- .../mgechev/revive/rule/context-as-argument.go | 80 +++- .../mgechev/revive/rule/context-keys-type.go | 6 +- .../github.com/mgechev/revive/rule/cyclomatic.go | 38 +- vendor/github.com/mgechev/revive/rule/datarace.go | 142 +++++++ vendor/github.com/mgechev/revive/rule/deep-exit.go | 12 +- vendor/github.com/mgechev/revive/rule/defer.go | 61 ++- .../github.com/mgechev/revive/rule/dot-imports.go | 4 +- .../mgechev/revive/rule/duplicated-imports.go | 4 +- .../github.com/mgechev/revive/rule/early-return.go | 4 +- .../github.com/mgechev/revive/rule/empty-block.go | 6 +- .../github.com/mgechev/revive/rule/empty-lines.go | 88 ++-- .../github.com/mgechev/revive/rule/error-naming.go | 4 +- .../github.com/mgechev/revive/rule/error-return.go | 4 +- .../mgechev/revive/rule/error-strings.go | 127 +++++- vendor/github.com/mgechev/revive/rule/errorf.go | 4 +- vendor/github.com/mgechev/revive/rule/exported.go | 94 ++++- .../github.com/mgechev/revive/rule/file-header.go | 41 +- .../github.com/mgechev/revive/rule/flag-param.go | 7 +- .../mgechev/revive/rule/function-length.go | 168 ++++++++ .../mgechev/revive/rule/function-result-limit.go | 39 +- .../github.com/mgechev/revive/rule/get-return.go | 4 +- .../mgechev/revive/rule/identical-branches.go | 6 +- vendor/github.com/mgechev/revive/rule/if-return.go | 4 +- .../mgechev/revive/rule/import-shadowing.go | 18 +- .../mgechev/revive/rule/imports-blacklist.go | 59 ++- .../mgechev/revive/rule/increment-decrement.go | 5 +- .../mgechev/revive/rule/indent-error-flow.go | 4 +- .../mgechev/revive/rule/line-length-limit.go | 37 +- .../mgechev/revive/rule/max-public-structs.go | 34 +- .../mgechev/revive/rule/modifies-param.go | 4 +- .../mgechev/revive/rule/modifies-value-receiver.go | 4 +- .../mgechev/revive/rule/nested-structs.go | 67 +++ .../mgechev/revive/rule/optimize-operands-order.go | 77 ++++ .../mgechev/revive/rule/package-comments.go | 64 ++- .../mgechev/revive/rule/range-val-address.go | 69 +++- .../mgechev/revive/rule/range-val-in-closure.go | 20 +- vendor/github.com/mgechev/revive/rule/range.go | 4 +- .../mgechev/revive/rule/receiver-naming.go | 8 +- .../mgechev/revive/rule/redefines-builtin-id.go | 187 +++++---- .../mgechev/revive/rule/string-format.go | 281 +++++++++++++ .../mgechev/revive/rule/string-of-int.go | 6 +- .../github.com/mgechev/revive/rule/struct-tag.go | 140 ++++++- .../mgechev/revive/rule/superfluous-else.go | 16 +- .../github.com/mgechev/revive/rule/time-equal.go | 76 ++++ .../github.com/mgechev/revive/rule/time-naming.go | 12 +- .../mgechev/revive/rule/unconditional-recursion.go | 16 +- .../mgechev/revive/rule/unexported-naming.go | 4 +- .../mgechev/revive/rule/unexported-return.go | 17 +- .../mgechev/revive/rule/unhandled-error.go | 38 +- .../mgechev/revive/rule/unnecessary-stmt.go | 4 +- .../mgechev/revive/rule/unreachable-code.go | 18 +- .../github.com/mgechev/revive/rule/unused-param.go | 4 +- vendor/github.com/mgechev/revive/rule/use-any.go | 54 +++ .../mgechev/revive/rule/useless-break.go | 82 ++++ vendor/github.com/mgechev/revive/rule/utils.go | 46 +-- .../mgechev/revive/rule/var-declarations.go | 4 +- .../github.com/mgechev/revive/rule/var-naming.go | 66 +-- .../mgechev/revive/rule/waitgroup-by-value.go | 4 +- 92 files changed, 2696 insertions(+), 1310 deletions(-) delete mode 100644 vendor/github.com/mgechev/dots/.travis.yml delete mode 100644 vendor/github.com/mgechev/dots/LICENSE delete mode 100644 vendor/github.com/mgechev/dots/README.md delete mode 100644 vendor/github.com/mgechev/dots/resolve.go create mode 100644 vendor/github.com/mgechev/revive/formatter/sarif.go create mode 100644 vendor/github.com/mgechev/revive/internal/typeparams/typeparams.go create mode 100644 vendor/github.com/mgechev/revive/internal/typeparams/typeparams_go117.go create mode 100644 vendor/github.com/mgechev/revive/internal/typeparams/typeparams_go118.go create mode 100644 vendor/github.com/mgechev/revive/rule/banned-characters.go create mode 100644 vendor/github.com/mgechev/revive/rule/datarace.go create mode 100644 vendor/github.com/mgechev/revive/rule/function-length.go create mode 100644 vendor/github.com/mgechev/revive/rule/nested-structs.go create mode 100644 vendor/github.com/mgechev/revive/rule/optimize-operands-order.go create mode 100644 vendor/github.com/mgechev/revive/rule/string-format.go create mode 100644 vendor/github.com/mgechev/revive/rule/time-equal.go create mode 100644 vendor/github.com/mgechev/revive/rule/use-any.go create mode 100644 vendor/github.com/mgechev/revive/rule/useless-break.go (limited to 'vendor/github.com/mgechev') diff --git a/vendor/github.com/mgechev/dots/.travis.yml b/vendor/github.com/mgechev/dots/.travis.yml deleted file mode 100644 index f4a4a7363..000000000 --- a/vendor/github.com/mgechev/dots/.travis.yml +++ /dev/null @@ -1,2 +0,0 @@ -language: go -go: master diff --git a/vendor/github.com/mgechev/dots/LICENSE b/vendor/github.com/mgechev/dots/LICENSE deleted file mode 100644 index c617c7e01..000000000 --- a/vendor/github.com/mgechev/dots/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Minko Gechev - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/mgechev/dots/README.md b/vendor/github.com/mgechev/dots/README.md deleted file mode 100644 index 1203aef5f..000000000 --- a/vendor/github.com/mgechev/dots/README.md +++ /dev/null @@ -1,100 +0,0 @@ -[![Build Status](https://travis-ci.org/mgechev/dots.svg?branch=master)](https://travis-ci.org/mgechev/dots) - -# Dots - -Implements the wildcard file matching in Go used by golint, go test etc. - -## Usage - -```go -import "github.com/mgechev/dots" - -func main() { - result, err := dots.Resolve([]string{"./fixtures/..."}, []string{"./fixtures/foo"}) - for _, f := range result { - fmt.Println(f); - } -} -``` - -If we suppose that we have the following directory structure: - -```text -├── README.md -├── fixtures -│   ├── bar -│   │   ├── bar1.go -│   │   └── bar2.go -│   ├── baz -│   │   ├── baz1.go -│   │   ├── baz2.go -│   │   └── baz3.go -│   └── foo -│   ├── foo1.go -│   ├── foo2.go -│   └── foo3.go -└── main.go -``` - -The result will be: - -```text -fixtures/bar/bar1.go -fixtures/bar/bar2.go -fixtures/baz/baz1.go -fixtures/baz/baz2.go -fixtures/baz/baz3.go -``` - -`dots` supports wildcard in both - the first and the last argument of `Resolve`, which means that you can ignore files based on a wildcard: - -```go -dots.Resolve([]string{"github.com/mgechev/dots"}, []string{"./..."}) // empty list -dots.Resolve([]string{"./fixtures/bar/..."}, []string{"./fixture/foo/...", "./fixtures/baz/..."}) // bar1.go, bar2.go -``` - -## Preserve package structure - -`dots` allow you to receive a slice of slices where each nested slice represents an individual package: - -```go -dots.ResolvePackages([]string{"github.com/mgechev/dots/..."}, []string{}) -``` - -So we will get the result: - -```text -[ - [ - "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/bar/bar1.go", - "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/bar/bar2.go" - ], - [ - "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/baz/baz1.go", - "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/baz/baz2.go", - "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/baz/baz3.go" - ], - [ - "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/foo/foo1.go", - "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/foo/foo2.go", - "$GOROOT/src/github.com/mgechev/dots/fixtures/dummy/foo/foo3.go" - ], - [ - "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/baz/baz1.go", - "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/baz/baz2.go" - ], - [ - "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/foo/foo1.go", - "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/foo/foo2.go" - ], - [ - "$GOROOT/src/github.com/mgechev/dots/fixtures/pkg/foo/bar/bar1.go" - ] -] -``` - -This method is especially useful, when you want to perform type checking over given package from the result. - -## License - -MIT diff --git a/vendor/github.com/mgechev/dots/resolve.go b/vendor/github.com/mgechev/dots/resolve.go deleted file mode 100644 index 309ba18ad..000000000 --- a/vendor/github.com/mgechev/dots/resolve.go +++ /dev/null @@ -1,456 +0,0 @@ -package dots - -import ( - "go/build" - "log" - "os" - "path" - "path/filepath" - "regexp" - "runtime" - "strings" -) - -var ( - buildContext = build.Default - goroot = filepath.Clean(runtime.GOROOT()) - gorootSrc = filepath.Join(goroot, "src") -) - -func flatten(arr [][]string) []string { - var res []string - for _, e := range arr { - res = append(res, e...) - } - return res -} - -// Resolve accepts a slice of paths with optional "..." placeholder and a slice with paths to be skipped. -// The final result is the set of all files from the selected directories subtracted with -// the files in the skip slice. -func Resolve(includePatterns, skipPatterns []string) ([]string, error) { - skip, err := resolvePatterns(skipPatterns) - filter := newPathFilter(flatten(skip)) - if err != nil { - return nil, err - } - - pathSet := map[string]bool{} - includePackages, err := resolvePatterns(includePatterns) - include := flatten(includePackages) - if err != nil { - return nil, err - } - - var result []string - for _, i := range include { - if _, ok := pathSet[i]; !ok && !filter(i) { - pathSet[i] = true - result = append(result, i) - } - } - return result, err -} - -// ResolvePackages accepts a slice of paths with optional "..." placeholder and a slice with paths to be skipped. -// The final result is the set of all files from the selected directories subtracted with -// the files in the skip slice. The difference between `Resolve` and `ResolvePackages` -// is that `ResolvePackages` preserves the package structure in the nested slices. -func ResolvePackages(includePatterns, skipPatterns []string) ([][]string, error) { - skip, err := resolvePatterns(skipPatterns) - filter := newPathFilter(flatten(skip)) - if err != nil { - return nil, err - } - - pathSet := map[string]bool{} - include, err := resolvePatterns(includePatterns) - if err != nil { - return nil, err - } - - var result [][]string - for _, p := range include { - var packageFiles []string - for _, f := range p { - if _, ok := pathSet[f]; !ok && !filter(f) { - pathSet[f] = true - packageFiles = append(packageFiles, f) - } - } - result = append(result, packageFiles) - } - return result, err -} - -func isDir(filename string) bool { - fi, err := os.Stat(filename) - return err == nil && fi.IsDir() -} - -func exists(filename string) bool { - _, err := os.Stat(filename) - return err == nil -} - -func resolveDir(dirname string) ([]string, error) { - pkg, err := build.ImportDir(dirname, 0) - return resolveImportedPackage(pkg, err) -} - -func resolvePackage(pkgname string) ([]string, error) { - pkg, err := build.Import(pkgname, ".", 0) - return resolveImportedPackage(pkg, err) -} - -func resolveImportedPackage(pkg *build.Package, err error) ([]string, error) { - if err != nil { - if _, nogo := err.(*build.NoGoError); nogo { - // Don't complain if the failure is due to no Go source files. - return nil, nil - } - return nil, err - } - - var files []string - files = append(files, pkg.GoFiles...) - files = append(files, pkg.CgoFiles...) - files = append(files, pkg.TestGoFiles...) - if pkg.Dir != "." { - for i, f := range files { - files[i] = filepath.Join(pkg.Dir, f) - } - } - return files, nil -} - -func resolvePatterns(patterns []string) ([][]string, error) { - var files [][]string - for _, pattern := range patterns { - f, err := resolvePattern(pattern) - if err != nil { - return nil, err - } - files = append(files, f...) - } - return files, nil -} - -func resolvePattern(pattern string) ([][]string, error) { - // dirsRun, filesRun, and pkgsRun indicate whether golint is applied to - // directory, file or package targets. The distinction affects which - // checks are run. It is no valid to mix target types. - var dirsRun, filesRun, pkgsRun int - var matches []string - - if strings.HasSuffix(pattern, "/...") && isDir(pattern[:len(pattern)-len("/...")]) { - dirsRun = 1 - for _, dirname := range matchPackagesInFS(pattern) { - matches = append(matches, dirname) - } - } else if isDir(pattern) { - dirsRun = 1 - matches = append(matches, pattern) - } else if exists(pattern) { - filesRun = 1 - matches = append(matches, pattern) - } else { - pkgsRun = 1 - matches = append(matches, pattern) - } - - result := [][]string{} - switch { - case dirsRun == 1: - for _, dir := range matches { - res, err := resolveDir(dir) - if err != nil { - return nil, err - } - result = append(result, res) - } - case filesRun == 1: - return [][]string{matches}, nil - case pkgsRun == 1: - for _, pkg := range importPaths(matches) { - res, err := resolvePackage(pkg) - if err != nil { - return nil, err - } - result = append(result, res) - } - } - return result, nil -} - -func newPathFilter(skip []string) func(string) bool { - filter := map[string]bool{} - for _, name := range skip { - filter[name] = true - } - - return func(path string) bool { - base := filepath.Base(path) - if filter[base] || filter[path] { - return true - } - return base != "." && base != ".." && strings.ContainsAny(base[0:1], "_.") - } -} - -// importPathsNoDotExpansion returns the import paths to use for the given -// command line, but it does no ... expansion. -func importPathsNoDotExpansion(args []string) []string { - if len(args) == 0 { - return []string{"."} - } - var out []string - for _, a := range args { - // Arguments are supposed to be import paths, but - // as a courtesy to Windows developers, rewrite \ to / - // in command-line arguments. Handles .\... and so on. - if filepath.Separator == '\\' { - a = strings.Replace(a, `\`, `/`, -1) - } - - // Put argument in canonical form, but preserve leading ./. - if strings.HasPrefix(a, "./") { - a = "./" + path.Clean(a) - if a == "./." { - a = "." - } - } else { - a = path.Clean(a) - } - if a == "all" || a == "std" { - out = append(out, matchPackages(a)...) - continue - } - out = append(out, a) - } - return out -} - -// importPaths returns the import paths to use for the given command line. -func importPaths(args []string) []string { - args = importPathsNoDotExpansion(args) - var out []string - for _, a := range args { - if strings.Contains(a, "...") { - if build.IsLocalImport(a) { - out = append(out, matchPackagesInFS(a)...) - } else { - out = append(out, matchPackages(a)...) - } - continue - } - out = append(out, a) - } - return out -} - -// matchPattern(pattern)(name) reports whether -// name matches pattern. Pattern is a limited glob -// pattern in which '...' means 'any string' and there -// is no other special syntax. -func matchPattern(pattern string) func(name string) bool { - re := regexp.QuoteMeta(pattern) - re = strings.Replace(re, `\.\.\.`, `.*`, -1) - // Special case: foo/... matches foo too. - if strings.HasSuffix(re, `/.*`) { - re = re[:len(re)-len(`/.*`)] + `(/.*)?` - } - reg := regexp.MustCompile(`^` + re + `$`) - return func(name string) bool { - return reg.MatchString(name) - } -} - -// hasPathPrefix reports whether the path s begins with the -// elements in prefix. -func hasPathPrefix(s, prefix string) bool { - switch { - default: - return false - case len(s) == len(prefix): - return s == prefix - case len(s) > len(prefix): - if prefix != "" && prefix[len(prefix)-1] == '/' { - return strings.HasPrefix(s, prefix) - } - return s[len(prefix)] == '/' && s[:len(prefix)] == prefix - } -} - -// treeCanMatchPattern(pattern)(name) reports whether -// name or children of name can possibly match pattern. -// Pattern is the same limited glob accepted by matchPattern. -func treeCanMatchPattern(pattern string) func(name string) bool { - wildCard := false - if i := strings.Index(pattern, "..."); i >= 0 { - wildCard = true - pattern = pattern[:i] - } - return func(name string) bool { - return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || - wildCard && strings.HasPrefix(name, pattern) - } -} - -func matchPackages(pattern string) []string { - match := func(string) bool { return true } - treeCanMatch := func(string) bool { return true } - if pattern != "all" && pattern != "std" { - match = matchPattern(pattern) - treeCanMatch = treeCanMatchPattern(pattern) - } - - have := map[string]bool{ - "builtin": true, // ignore pseudo-package that exists only for documentation - } - if !buildContext.CgoEnabled { - have["runtime/cgo"] = true // ignore during walk - } - var pkgs []string - - // Commands - cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator) - filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error { - if err != nil || !fi.IsDir() || path == cmd { - return nil - } - name := path[len(cmd):] - if !treeCanMatch(name) { - return filepath.SkipDir - } - // Commands are all in cmd/, not in subdirectories. - if strings.Contains(name, string(filepath.Separator)) { - return filepath.SkipDir - } - - // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. - name = "cmd/" + name - if have[name] { - return nil - } - have[name] = true - if !match(name) { - return nil - } - _, err = buildContext.ImportDir(path, 0) - if err != nil { - if _, noGo := err.(*build.NoGoError); !noGo { - log.Print(err) - } - return nil - } - pkgs = append(pkgs, name) - return nil - }) - - for _, src := range buildContext.SrcDirs() { - if (pattern == "std" || pattern == "cmd") && src != gorootSrc { - continue - } - src = filepath.Clean(src) + string(filepath.Separator) - root := src - if pattern == "cmd" { - root += "cmd" + string(filepath.Separator) - } - filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { - if err != nil || !fi.IsDir() || path == src { - return nil - } - - // Avoid .foo, _foo, and testdata directory trees. - _, elem := filepath.Split(path) - if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { - return filepath.SkipDir - } - - name := filepath.ToSlash(path[len(src):]) - if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") { - // The name "std" is only the standard library. - // If the name is cmd, it's the root of the command tree. - return filepath.SkipDir - } - if !treeCanMatch(name) { - return filepath.SkipDir - } - if have[name] { - return nil - } - have[name] = true - if !match(name) { - return nil - } - _, err = buildContext.ImportDir(path, 0) - if err != nil { - if _, noGo := err.(*build.NoGoError); noGo { - return nil - } - } - pkgs = append(pkgs, name) - return nil - }) - } - return pkgs -} - -func matchPackagesInFS(pattern string) []string { - // Find directory to begin the scan. - // Could be smarter but this one optimization - // is enough for now, since ... is usually at the - // end of a path. - i := strings.Index(pattern, "...") - dir, _ := path.Split(pattern[:i]) - - // pattern begins with ./ or ../. - // path.Clean will discard the ./ but not the ../. - // We need to preserve the ./ for pattern matching - // and in the returned import paths. - prefix := "" - if strings.HasPrefix(pattern, "./") { - prefix = "./" - } - match := matchPattern(pattern) - - var pkgs []string - filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { - if err != nil || !fi.IsDir() { - return nil - } - if path == dir { - // filepath.Walk starts at dir and recurses. For the recursive case, - // the path is the result of filepath.Join, which calls filepath.Clean. - // The initial case is not Cleaned, though, so we do this explicitly. - // - // This converts a path like "./io/" to "io". Without this step, running - // "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io - // package, because prepending the prefix "./" to the unclean path would - // result in "././io", and match("././io") returns false. - path = filepath.Clean(path) - } - - // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". - _, elem := filepath.Split(path) - dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." - if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { - return filepath.SkipDir - } - - name := prefix + filepath.ToSlash(path) - if !match(name) { - return nil - } - if _, err = build.ImportDir(path, 0); err != nil { - if _, noGo := err.(*build.NoGoError); !noGo { - log.Print(err) - } - return nil - } - pkgs = append(pkgs, name) - return nil - }) - return pkgs -} diff --git a/vendor/github.com/mgechev/revive/config/config.go b/vendor/github.com/mgechev/revive/config/config.go index 1357bc744..5c63f35b3 100644 --- a/vendor/github.com/mgechev/revive/config/config.go +++ b/vendor/github.com/mgechev/revive/config/config.go @@ -20,7 +20,6 @@ var defaultRules = []lint.Rule{ &rule.ExportedRule{}, &rule.VarNamingRule{}, &rule.IndentErrorFlowRule{}, - &rule.IfReturnRule{}, &rule.RangeRule{}, &rule.ErrorfRule{}, &rule.ErrorNamingRule{}, @@ -72,11 +71,21 @@ var allRules = append([]lint.Rule{ &rule.UnhandledErrorRule{}, &rule.CognitiveComplexityRule{}, &rule.StringOfIntRule{}, + &rule.StringFormatRule{}, &rule.EarlyReturnRule{}, &rule.UnconditionalRecursionRule{}, &rule.IdenticalBranchesRule{}, &rule.DeferRule{}, &rule.UnexportedNamingRule{}, + &rule.FunctionLength{}, + &rule.NestedStructs{}, + &rule.IfReturnRule{}, + &rule.UselessBreak{}, + &rule.TimeEqualRule{}, + &rule.BannedCharsRule{}, + &rule.OptimizeOperandsOrderRule{}, + &rule.UseAnyRule{}, + &rule.DataRaceRule{}, }, defaultRules...) var allFormatters = []lint.Formatter{ @@ -88,6 +97,7 @@ var allFormatters = []lint.Formatter{ &formatter.Unix{}, &formatter.Checkstyle{}, &formatter.Plain{}, + &formatter.Sarif{}, } func getFormatters() map[string]lint.Formatter { @@ -98,42 +108,65 @@ func getFormatters() map[string]lint.Formatter { return result } -// GetLintingRules yields the linting rules activated in the configuration -func GetLintingRules(config *lint.Config) ([]lint.Rule, error) { +// GetLintingRules yields the linting rules that must be applied by the linter +func GetLintingRules(config *lint.Config, extraRules []lint.Rule) ([]lint.Rule, error) { rulesMap := map[string]lint.Rule{} for _, r := range allRules { rulesMap[r.Name()] = r } + for _, r := range extraRules { + if _, ok := rulesMap[r.Name()]; ok { + continue + } + rulesMap[r.Name()] = r + } - lintingRules := []lint.Rule{} - for name := range config.Rules { - rule, ok := rulesMap[name] + var lintingRules []lint.Rule + for name, ruleConfig := range config.Rules { + r, ok := rulesMap[name] if !ok { return nil, fmt.Errorf("cannot find rule: %s", name) } - lintingRules = append(lintingRules, rule) + + if ruleConfig.Disabled { + continue // skip disabled rules + } + + lintingRules = append(lintingRules, r) } return lintingRules, nil } -func parseConfig(path string) (*lint.Config, error) { - config := &lint.Config{} +func parseConfig(path string, config *lint.Config) error { file, err := ioutil.ReadFile(path) if err != nil { - return nil, errors.New("cannot read the config file") + return errors.New("cannot read the config file") } _, err = toml.Decode(string(file), config) if err != nil { - return nil, fmt.Errorf("cannot parse the config file: %v", err) + return fmt.Errorf("cannot parse the config file: %v", err) } - return config, nil + return nil } func normalizeConfig(config *lint.Config) { - if config.Confidence == 0 { - config.Confidence = 0.8 + if len(config.Rules) == 0 { + config.Rules = map[string]lint.RuleConfig{} } + if config.EnableAllRules { + // Add to the configuration all rules not yet present in it + for _, r := range allRules { + ruleName := r.Name() + _, alreadyInConf := config.Rules[ruleName] + if alreadyInConf { + continue + } + // Add the rule with an empty conf for + config.Rules[ruleName] = lint.RuleConfig{} + } + } + severity := config.Severity if severity != "" { for k, v := range config.Rules { @@ -151,16 +184,23 @@ func normalizeConfig(config *lint.Config) { } } +const defaultConfidence = 0.8 + // GetConfig yields the configuration func GetConfig(configPath string) (*lint.Config, error) { - config := defaultConfig() - if configPath != "" { - var err error - config, err = parseConfig(configPath) + config := &lint.Config{} + switch { + case configPath != "": + config.Confidence = defaultConfidence + err := parseConfig(configPath, config) if err != nil { return nil, err } + + default: // no configuration provided + config = defaultConfig() } + normalizeConfig(config) return config, nil } @@ -168,20 +208,20 @@ func GetConfig(configPath string) (*lint.Config, error) { // GetFormatter yields the formatter for lint failures func GetFormatter(formatterName string) (lint.Formatter, error) { formatters := getFormatters() - formatter := formatters["default"] + fmtr := formatters["default"] if formatterName != "" { f, ok := formatters[formatterName] if !ok { return nil, fmt.Errorf("unknown formatter %v", formatterName) } - formatter = f + fmtr = f } - return formatter, nil + return fmtr, nil } func defaultConfig() *lint.Config { defaultConfig := lint.Config{ - Confidence: 0.0, + Confidence: defaultConfidence, Severity: lint.SeverityWarning, Rules: map[string]lint.RuleConfig{}, } diff --git a/vendor/github.com/mgechev/revive/formatter/checkstyle.go b/vendor/github.com/mgechev/revive/formatter/checkstyle.go index bd20da888..33a3b2ca1 100644 --- a/vendor/github.com/mgechev/revive/formatter/checkstyle.go +++ b/vendor/github.com/mgechev/revive/formatter/checkstyle.go @@ -3,8 +3,9 @@ package formatter import ( "bytes" "encoding/xml" - "github.com/mgechev/revive/lint" plainTemplate "text/template" + + "github.com/mgechev/revive/lint" ) // Checkstyle is an implementation of the Formatter interface @@ -14,7 +15,7 @@ type Checkstyle struct { } // Name returns the name of the formatter -func (f *Checkstyle) Name() string { +func (*Checkstyle) Name() string { return "checkstyle" } @@ -28,8 +29,8 @@ type issue struct { } // Format formats the failures gotten from the lint. -func (f *Checkstyle) Format(failures <-chan lint.Failure, config lint.Config) (string, error) { - var issues = map[string][]issue{} +func (*Checkstyle) Format(failures <-chan lint.Failure, config lint.Config) (string, error) { + issues := map[string][]issue{} for failure := range failures { buf := new(bytes.Buffer) xml.Escape(buf, []byte(failure.Failure)) diff --git a/vendor/github.com/mgechev/revive/formatter/default.go b/vendor/github.com/mgechev/revive/formatter/default.go index 145e6d548..f76a7b29a 100644 --- a/vendor/github.com/mgechev/revive/formatter/default.go +++ b/vendor/github.com/mgechev/revive/formatter/default.go @@ -13,12 +13,12 @@ type Default struct { } // Name returns the name of the formatter -func (f *Default) Name() string { +func (*Default) Name() string { return "default" } // Format formats the failures gotten from the lint. -func (f *Default) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) { +func (*Default) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) { for failure := range failures { fmt.Printf("%v: %s\n", failure.Position.Start, failure.Failure) } diff --git a/vendor/github.com/mgechev/revive/formatter/friendly.go b/vendor/github.com/mgechev/revive/formatter/friendly.go index d0a3099f8..ced8fa46c 100644 --- a/vendor/github.com/mgechev/revive/formatter/friendly.go +++ b/vendor/github.com/mgechev/revive/formatter/friendly.go @@ -10,16 +10,6 @@ import ( "github.com/olekukonko/tablewriter" ) -var newLines = map[rune]bool{ - 0x000A: true, - 0x000B: true, - 0x000C: true, - 0x000D: true, - 0x0085: true, - 0x2028: true, - 0x2029: true, -} - func getErrorEmoji() string { return color.RedString("✘") } @@ -35,7 +25,7 @@ type Friendly struct { } // Name returns the name of the formatter -func (f *Friendly) Name() string { +func (*Friendly) Name() string { return "friendly" } @@ -49,11 +39,11 @@ func (f *Friendly) Format(failures <-chan lint.Failure, config lint.Config) (str sev := severity(config, failure) f.printFriendlyFailure(failure, sev) if sev == lint.SeverityWarning { - warningMap[failure.RuleName] = warningMap[failure.RuleName] + 1 + warningMap[failure.RuleName]++ totalWarnings++ } if sev == lint.SeverityError { - errorMap[failure.RuleName] = errorMap[failure.RuleName] + 1 + errorMap[failure.RuleName]++ totalErrors++ } } @@ -78,7 +68,7 @@ func (f *Friendly) printHeaderRow(failure lint.Failure, severity lint.Severity) fmt.Print(f.table([][]string{{emoji, "https://revive.run/r#" + failure.RuleName, color.GreenString(failure.Failure)}})) } -func (f *Friendly) printFilePosition(failure lint.Failure) { +func (*Friendly) printFilePosition(failure lint.Failure) { fmt.Printf(" %s:%d:%d", failure.GetFilename(), failure.Position.Start.Line, failure.Position.Start.Column) } @@ -87,7 +77,7 @@ type statEntry struct { failures int } -func (f *Friendly) printSummary(errors, warnings int) { +func (*Friendly) printSummary(errors, warnings int) { emoji := getWarningEmoji() if errors > 0 { emoji = getErrorEmoji() @@ -136,7 +126,7 @@ func (f *Friendly) printStatistics(header string, stats map[string]int) { fmt.Println(f.table(formatted)) } -func (f *Friendly) table(rows [][]string) string { +func (*Friendly) table(rows [][]string) string { buf := new(bytes.Buffer) table := tablewriter.NewWriter(buf) table.SetBorder(false) diff --git a/vendor/github.com/mgechev/revive/formatter/json.go b/vendor/github.com/mgechev/revive/formatter/json.go index 9c939face..7cace89ec 100644 --- a/vendor/github.com/mgechev/revive/formatter/json.go +++ b/vendor/github.com/mgechev/revive/formatter/json.go @@ -13,7 +13,7 @@ type JSON struct { } // Name returns the name of the formatter -func (f *JSON) Name() string { +func (*JSON) Name() string { return "json" } @@ -24,7 +24,7 @@ type jsonObject struct { } // Format formats the failures gotten from the lint. -func (f *JSON) Format(failures <-chan lint.Failure, config lint.Config) (string, error) { +func (*JSON) Format(failures <-chan lint.Failure, config lint.Config) (string, error) { var slice []jsonObject for failure := range failures { obj := jsonObject{} diff --git a/vendor/github.com/mgechev/revive/formatter/ndjson.go b/vendor/github.com/mgechev/revive/formatter/ndjson.go index aa2b1d636..a02d9c80f 100644 --- a/vendor/github.com/mgechev/revive/formatter/ndjson.go +++ b/vendor/github.com/mgechev/revive/formatter/ndjson.go @@ -14,12 +14,12 @@ type NDJSON struct { } // Name returns the name of the formatter -func (f *NDJSON) Name() string { +func (*NDJSON) Name() string { return "ndjson" } // Format formats the failures gotten from the lint. -func (f *NDJSON) Format(failures <-chan lint.Failure, config lint.Config) (string, error) { +func (*NDJSON) Format(failures <-chan lint.Failure, config lint.Config) (string, error) { enc := json.NewEncoder(os.Stdout) for failure := range failures { obj := jsonObject{} diff --git a/vendor/github.com/mgechev/revive/formatter/plain.go b/vendor/github.com/mgechev/revive/formatter/plain.go index a854d2562..6e083bcfd 100644 --- a/vendor/github.com/mgechev/revive/formatter/plain.go +++ b/vendor/github.com/mgechev/revive/formatter/plain.go @@ -13,12 +13,12 @@ type Plain struct { } // Name returns the name of the formatter -func (f *Plain) Name() string { +func (*Plain) Name() string { return "plain" } // Format formats the failures gotten from the lint. -func (f *Plain) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) { +func (*Plain) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) { for failure := range failures { fmt.Printf("%v: %s %s\n", failure.Position.Start, failure.Failure, "https://revive.run/r#"+failure.RuleName) } diff --git a/vendor/github.com/mgechev/revive/formatter/sarif.go b/vendor/github.com/mgechev/revive/formatter/sarif.go new file mode 100644 index 000000000..ee62adcc0 --- /dev/null +++ b/vendor/github.com/mgechev/revive/formatter/sarif.go @@ -0,0 +1,107 @@ +package formatter + +import ( + "bytes" + "fmt" + "strings" + + "github.com/chavacava/garif" + "github.com/mgechev/revive/lint" +) + +// Sarif is an implementation of the Formatter interface +// which formats revive failures into SARIF format. +type Sarif struct { + Metadata lint.FormatterMetadata +} + +// Name returns the name of the formatter +func (*Sarif) Name() string { + return "sarif" +} + +const reviveSite = "https://revive.run" + +// Format formats the failures gotten from the lint. +func (*Sarif) Format(failures <-chan lint.Failure, cfg lint.Config) (string, error) { + sarifLog := newReviveRunLog(cfg) + + for failure := range failures { + sarifLog.AddResult(failure) + } + + buf := new(bytes.Buffer) + sarifLog.PrettyWrite(buf) + + return buf.String(), nil +} + +type reviveRunLog struct { + *garif.LogFile + run *garif.Run + rules map[string]lint.RuleConfig +} + +func newReviveRunLog(cfg lint.Config) *reviveRunLog { + run := garif.NewRun(garif.NewTool(garif.NewDriver("revive").WithInformationUri(reviveSite))) + log := garif.NewLogFile([]*garif.Run{run}, garif.Version210) + + reviveLog := &reviveRunLog{ + log, + run, + cfg.Rules, + } + + reviveLog.addRules(cfg.Rules) + + return reviveLog +} + +func (l *reviveRunLog) addRules(cfg map[string]lint.RuleConfig) { + for name, ruleCfg := range cfg { + rule := garif.NewRule(name).WithHelpUri(reviveSite + "/r#" + name) + setRuleProperties(rule, ruleCfg) + driver := l.run.Tool.Driver + + if driver.Rules == nil { + driver.Rules = []*garif.ReportingDescriptor{rule} + return + } + + driver.Rules = append(driver.Rules, rule) + } +} + +func (l *reviveRunLog) AddResult(failure lint.Failure) { + positiveOrZero := func(x int) int { + if x > 0 { + return x + } + return 0 + } + position := failure.Position + filename := position.Start.Filename + line := positiveOrZero(position.Start.Line - 1) // https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#def_line + column := positiveOrZero(position.Start.Column - 1) // https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#def_column + + result := garif.NewResult(garif.NewMessageFromText(failure.Failure)) + location := garif.NewLocation().WithURI(filename).WithLineColumn(line, column) + result.Locations = append(result.Locations, location) + result.RuleId = failure.RuleName + result.Level = l.rules[failure.RuleName].Severity + + l.run.Results = append(l.run.Results, result) +} + +func setRuleProperties(sarifRule *garif.ReportingDescriptor, lintRule lint.RuleConfig) { + arguments := make([]string, len(lintRule.Arguments)) + for i, arg := range lintRule.Arguments { + arguments[i] = fmt.Sprintf("%+v", arg) + } + + if len(arguments) > 0 { + sarifRule.WithProperties("arguments", strings.Join(arguments, ",")) + } + + sarifRule.WithProperties("severity", string(lintRule.Severity)) +} diff --git a/vendor/github.com/mgechev/revive/formatter/stylish.go b/vendor/github.com/mgechev/revive/formatter/stylish.go index cd81fdae7..828228c72 100644 --- a/vendor/github.com/mgechev/revive/formatter/stylish.go +++ b/vendor/github.com/mgechev/revive/formatter/stylish.go @@ -16,7 +16,7 @@ type Stylish struct { } // Name returns the name of the formatter -func (f *Stylish) Name() string { +func (*Stylish) Name() string { return "stylish" } @@ -32,10 +32,10 @@ func formatFailure(failure lint.Failure, severity lint.Severity) []string { } // Format formats the failures gotten from the lint. -func (f *Stylish) Format(failures <-chan lint.Failure, config lint.Config) (string, error) { +func (*Stylish) Format(failures <-chan lint.Failure, config lint.Config) (string, error) { var result [][]string - var totalErrors = 0 - var total = 0 + totalErrors := 0 + total := 0 for f := range failures { total++ diff --git a/vendor/github.com/mgechev/revive/formatter/unix.go b/vendor/github.com/mgechev/revive/formatter/unix.go index b9ae62d38..ef2f1613a 100644 --- a/vendor/github.com/mgechev/revive/formatter/unix.go +++ b/vendor/github.com/mgechev/revive/formatter/unix.go @@ -14,12 +14,12 @@ type Unix struct { } // Name returns the name of the formatter -func (f *Unix) Name() string { +func (*Unix) Name() string { return "unix" } // Format formats the failures gotten from the lint. -func (f *Unix) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) { +func (*Unix) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) { for failure := range failures { fmt.Printf("%v: [%s] %s\n", failure.Position.Start, failure.RuleName, failure.Failure) } diff --git a/vendor/github.com/mgechev/revive/internal/typeparams/typeparams.go b/vendor/github.com/mgechev/revive/internal/typeparams/typeparams.go new file mode 100644 index 000000000..33c4f203e --- /dev/null +++ b/vendor/github.com/mgechev/revive/internal/typeparams/typeparams.go @@ -0,0 +1,29 @@ +// Package typeparams provides utilities for working with Go ASTs with support +// for type parameters when built with Go 1.18 and higher. +package typeparams + +import ( + "go/ast" +) + +// Enabled reports whether type parameters are enabled in the current build +// environment. +func Enabled() bool { + return enabled +} + +// ReceiverType returns the named type of the method receiver, sans "*" and type +// parameters, or "invalid-type" if fn.Recv is ill formed. +func ReceiverType(fn *ast.FuncDecl) string { + e := fn.Recv.List[0].Type + if s, ok := e.(*ast.StarExpr); ok { + e = s.X + } + if enabled { + e = unpackIndexExpr(e) + } + if id, ok := e.(*ast.Ident); ok { + return id.Name + } + return "invalid-type" +} diff --git a/vendor/github.com/mgechev/revive/internal/typeparams/typeparams_go117.go b/vendor/github.com/mgechev/revive/internal/typeparams/typeparams_go117.go new file mode 100644 index 000000000..913a7316e --- /dev/null +++ b/vendor/github.com/mgechev/revive/internal/typeparams/typeparams_go117.go @@ -0,0 +1,12 @@ +//go:build !go1.18 +// +build !go1.18 + +package typeparams + +import ( + "go/ast" +) + +const enabled = false + +func unpackIndexExpr(e ast.Expr) ast.Expr { return e } diff --git a/vendor/github.com/mgechev/revive/internal/typeparams/typeparams_go118.go b/vendor/github.com/mgechev/revive/internal/typeparams/typeparams_go118.go new file mode 100644 index 000000000..0f7fd88d3 --- /dev/null +++ b/vendor/github.com/mgechev/revive/internal/typeparams/typeparams_go118.go @@ -0,0 +1,20 @@ +//go:build go1.18 +// +build go1.18 + +package typeparams + +import ( + "go/ast" +) + +const enabled = true + +func unpackIndexExpr(e ast.Expr) ast.Expr { + switch e := e.(type) { + case *ast.IndexExpr: + return e.X + case *ast.IndexListExpr: + return e.X + } + return e +} diff --git a/vendor/github.com/mgechev/revive/lint/config.go b/vendor/github.com/mgechev/revive/lint/config.go index fe65ace52..276305804 100644 --- a/vendor/github.com/mgechev/revive/lint/config.go +++ b/vendor/github.com/mgechev/revive/lint/config.go @@ -7,6 +7,7 @@ type Arguments = []interface{} type RuleConfig struct { Arguments Arguments Severity Severity + Disabled bool } // RulesConfig defines the config for all rules. @@ -25,8 +26,10 @@ type Config struct { IgnoreGeneratedHeader bool `toml:"ignoreGeneratedHeader"` Confidence float64 Severity Severity + EnableAllRules bool `toml:"enableAllRules"` Rules RulesConfig `toml:"rule"` ErrorCode int `toml:"errorCode"` WarningCode int `toml:"warningCode"` Directives DirectivesConfig `toml:"directive"` + Exclude []string `toml:"exclude"` } diff --git a/vendor/github.com/mgechev/revive/lint/file.go b/vendor/github.com/mgechev/revive/lint/file.go index 8bef9c220..dcf0e608f 100644 --- a/vendor/github.com/mgechev/revive/lint/file.go +++ b/vendor/github.com/mgechev/revive/lint/file.go @@ -47,7 +47,7 @@ func (f *File) ToPosition(pos token.Pos) token.Position { return f.Pkg.fset.Position(pos) } -// Render renters a node. +// Render renders a node. func (f *File) Render(x interface{}) string { var buf bytes.Buffer if err := printer.Fprint(&buf, f.Pkg.fset, x); err != nil { @@ -74,10 +74,10 @@ var basicTypeKinds = map[types.BasicKind]string{ // and indicates what its default type is. // scope may be nil. func (f *File) IsUntypedConst(expr ast.Expr) (defType string, ok bool) { - // Re-evaluate expr outside of its context to see if it's untyped. + // Re-evaluate expr outside its context to see if it's untyped. // (An expr evaluated within, for example, an assignment context will get the type of the LHS.) exprStr := f.Render(expr) - tv, err := types.Eval(f.Pkg.fset, f.Pkg.TypesPkg, expr.Pos(), exprStr) + tv, err := types.Eval(f.Pkg.fset, f.Pkg.TypesPkg(), expr.Pos(), exprStr) if err != nil { return "", false } @@ -91,10 +91,7 @@ func (f *File) IsUntypedConst(expr ast.Expr) (defType string, ok bool) { } func (f *File) isMain() bool { - if f.AST.Name.Name == "main" { - return true - } - return false + return f.AST.Name.Name == "main" } const directiveSpecifyDisableReason = "specify-disable-reason" @@ -129,11 +126,13 @@ type enableDisableConfig struct { position int } -const directiveRE = `^//[\s]*revive:(enable|disable)(?:-(line|next-line))?(?::([^\s]+))?[\s]*(?: (.+))?$` -const directivePos = 1 -const modifierPos = 2 -const rulesPos = 3 -const reasonPos = 4 +const ( + directiveRE = `^//[\s]*revive:(enable|disable)(?:-(line|next-line))?(?::([^\s]+))?[\s]*(?: (.+))?$` + directivePos = 1 + modifierPos = 2 + rulesPos = 3 + reasonPos = 4 +) var re = regexp.MustCompile(directiveRE) @@ -207,11 +206,11 @@ func (f *File) disabledIntervals(rules []Rule, mustSpecifyDisableReason bool, fa for _, c := range comments { match := re.FindStringSubmatch(c.Text) if len(match) == 0 { - return + continue } - ruleNames := []string{} tempNames := strings.Split(match[rulesPos], ",") + for _, name := range tempNames { name = strings.Trim(name, "\n") if len(name) > 0 { @@ -250,7 +249,7 @@ func (f *File) disabledIntervals(rules []Rule, mustSpecifyDisableReason bool, fa return getEnabledDisabledIntervals() } -func (f *File) filterFailures(failures []Failure, disabledIntervals disabledIntervalsMap) []Failure { +func (File) filterFailures(failures []Failure, disabledIntervals disabledIntervalsMap) []Failure { result := []Failure{} for _, failure := range failures { fStart := failure.Position.Start.Line diff --git a/vendor/github.com/mgechev/revive/lint/linter.go b/vendor/github.com/mgechev/revive/lint/linter.go index cdca84fb5..fb1ab6f28 100644 --- a/vendor/github.com/mgechev/revive/lint/linter.go +++ b/vendor/github.com/mgechev/revive/lint/linter.go @@ -6,6 +6,8 @@ import ( "fmt" "go/token" "os" + "regexp" + "strconv" "sync" ) @@ -16,12 +18,34 @@ type disabledIntervalsMap = map[string][]DisabledInterval // Linter is used for linting set of files. type Linter struct { - reader ReadFile + reader ReadFile + fileReadTokens chan struct{} } // New creates a new Linter -func New(reader ReadFile) Linter { - return Linter{reader: reader} +func New(reader ReadFile, maxOpenFiles int) Linter { + var fileReadTokens chan struct{} + if maxOpenFiles > 0 { + fileReadTokens = make(chan struct{}, maxOpenFiles) + } + return Linter{ + reader: reader, + fileReadTokens: fileReadTokens, + } +} + +func (l Linter) readFile(path string) (result []byte, err error) { + if l.fileReadTokens != nil { + // "take" a token by writing to the channel. + // It will block if no more space in the channel's buffer + l.fileReadTokens <- struct{}{} + defer func() { + // "free" a token by reading from the channel + <-l.fileReadTokens + }() + } + + return l.reader(path) } var ( @@ -57,20 +81,20 @@ func (l *Linter) lintPackage(filenames []string, ruleSet []Rule, config Config, pkg := &Package{ fset: token.NewFileSet(), files: map[string]*File{}, - mu: sync.Mutex{}, } for _, filename := range filenames { - content, err := l.reader(filename) + content, err := l.readFile(filename) if err != nil { return err } - if isGenerated(content) && !config.IgnoreGeneratedHeader { + if !config.IgnoreGeneratedHeader && isGenerated(content) { continue } file, err := NewFile(filename, content, pkg) if err != nil { - return err + addInvalidFileFailure(filename, err.Error(), failures) + continue } pkg.files[filename] = file } @@ -97,3 +121,43 @@ func isGenerated(src []byte) bool { } return false } + +// addInvalidFileFailure adds a failure for an invalid formatted file +func addInvalidFileFailure(filename, errStr string, failures chan Failure) { + position := getPositionInvalidFile(filename, errStr) + failures <- Failure{ + Confidence: 1, + Failure: fmt.Sprintf("invalid file %s: %v", filename, errStr), + Category: "validity", + Position: position, + } +} + +// errPosRegexp matches with an NewFile error message +// i.e. : corrupted.go:10:4: expected '}', found 'EOF +// first group matches the line and the second group, the column +var errPosRegexp = regexp.MustCompile(`.*:(\d*):(\d*):.*$`) + +// getPositionInvalidFile gets the position of the error in an invalid file +func getPositionInvalidFile(filename, s string) FailurePosition { + pos := errPosRegexp.FindStringSubmatch(s) + if len(pos) < 3 { + return FailurePosition{} + } + line, err := strconv.Atoi(pos[1]) + if err != nil { + return FailurePosition{} + } + column, err := strconv.Atoi(pos[2]) + if err != nil { + return FailurePosition{} + } + + return FailurePosition{ + Start: token.Position{ + Filename: filename, + Line: line, + Column: column, + }, + } +} diff --git a/vendor/github.com/mgechev/revive/lint/package.go b/vendor/github.com/mgechev/revive/lint/package.go index 7b6046fd7..5976acf99 100644 --- a/vendor/github.com/mgechev/revive/lint/package.go +++ b/vendor/github.com/mgechev/revive/lint/package.go @@ -2,11 +2,12 @@ package lint import ( "go/ast" + "go/importer" "go/token" "go/types" "sync" - "golang.org/x/tools/go/gcexportdata" + "github.com/mgechev/revive/internal/typeparams" ) // Package represents a package in the project. @@ -14,18 +15,14 @@ type Package struct { fset *token.FileSet files map[string]*File - TypesPkg *types.Package - TypesInfo *types.Info + typesPkg *types.Package + typesInfo *types.Info // sortable is the set of types in the package that implement sort.Interface. - Sortable map[string]bool + sortable map[string]bool // main is whether this is a "main" package. main int - mu sync.Mutex -} - -var newImporter = func(fset *token.FileSet) types.ImporterFrom { - return gcexportdata.NewImporter(fset, make(map[string]*types.Package)) + sync.RWMutex } var ( @@ -34,8 +31,16 @@ var ( notSet = 3 ) +// Files return package's files. +func (p *Package) Files() map[string]*File { + return p.files +} + // IsMain returns if that's the main package. func (p *Package) IsMain() bool { + p.Lock() + defer p.Unlock() + if p.main == trueValue { return true } else if p.main == falseValue { @@ -51,19 +56,41 @@ func (p *Package) IsMain() bool { return false } +// TypesPkg yields information on this package +func (p *Package) TypesPkg() *types.Package { + p.RLock() + defer p.RUnlock() + return p.typesPkg +} + +// TypesInfo yields type information of this package identifiers +func (p *Package) TypesInfo() *types.Info { + p.RLock() + defer p.RUnlock() + return p.typesInfo +} + +// Sortable yields a map of sortable types in this package +func (p *Package) Sortable() map[string]bool { + p.RLock() + defer p.RUnlock() + return p.sortable +} + // TypeCheck performs type checking for given package. func (p *Package) TypeCheck() error { - p.mu.Lock() + p.Lock() + defer p.Unlock() + // If type checking has already been performed // skip it. - if p.TypesInfo != nil || p.TypesPkg != nil { - p.mu.Unlock() + if p.typesInfo != nil || p.typesPkg != nil { return nil } config := &types.Config{ // By setting a no-op error reporter, the type checker does as much work as possible. Error: func(error) {}, - Importer: newImporter(p.fset), + Importer: importer.Default(), } info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), @@ -82,9 +109,9 @@ func (p *Package) TypeCheck() error { // Remember the typechecking info, even if config.Check failed, // since we will get partial information. - p.TypesPkg = typesPkg - p.TypesInfo = info - p.mu.Unlock() + p.typesPkg = typesPkg + p.typesInfo = info + return err } @@ -104,10 +131,10 @@ func check(config *types.Config, n string, fset *token.FileSet, astFiles []*ast. // TypeOf returns the type of an expression. func (p *Package) TypeOf(expr ast.Expr) types.Type { - if p.TypesInfo == nil { + if p.typesInfo == nil { return nil } - return p.TypesInfo.TypeOf(expr) + return p.typesInfo.TypeOf(expr) } type walker struct { @@ -121,7 +148,7 @@ func (w *walker) Visit(n ast.Node) ast.Visitor { return w } // TODO(dsymonds): We could check the signature to be more precise. - recv := receiverType(fn) + recv := typeparams.ReceiverType(fn) if i, ok := w.nmap[fn.Name.Name]; ok { w.has[recv] |= i } @@ -129,7 +156,7 @@ func (w *walker) Visit(n ast.Node) ast.Visitor { } func (p *Package) scanSortable() { - p.Sortable = make(map[string]bool) + p.sortable = make(map[string]bool) // bitfield for which methods exist on each type. const ( @@ -144,24 +171,9 @@ func (p *Package) scanSortable() { } for typ, ms := range has { if ms == Len|Less|Swap { - p.Sortable[typ] = true - } - } -} - -// receiverType returns the named type of the method receiver, sans "*", -// or "invalid-type" if fn.Recv is ill formed. -func receiverType(fn *ast.FuncDecl) string { - switch e := fn.Recv.List[0].Type.(type) { - case *ast.Ident: - return e.Name - case *ast.StarExpr: - if id, ok := e.X.(*ast.Ident); ok { - return id.Name + p.sortable[typ] = true } } - // The parser accepts much more than just the legal forms. - return "invalid-type" } func (p *Package) lint(rules []Rule, config Config, failures chan Failure) { diff --git a/vendor/github.com/mgechev/revive/lint/rule.go b/vendor/github.com/mgechev/revive/lint/rule.go index 815abfdd8..ccc66691c 100644 --- a/vendor/github.com/mgechev/revive/lint/rule.go +++ b/vendor/github.com/mgechev/revive/lint/rule.go @@ -11,7 +11,7 @@ type DisabledInterval struct { RuleName string } -// Rule defines an abstract rule interaface +// Rule defines an abstract rule interface type Rule interface { Name() string Apply(*File, Arguments) []Failure @@ -23,7 +23,7 @@ type AbstractRule struct { } // ToFailurePosition returns the failure position. -func ToFailurePosition(start token.Pos, end token.Pos, file *File) FailurePosition { +func ToFailurePosition(start, end token.Pos, file *File) FailurePosition { return FailurePosition{ Start: file.ToPosition(start), End: file.ToPosition(end), diff --git a/vendor/github.com/mgechev/revive/rule/add-constant.go b/vendor/github.com/mgechev/revive/rule/add-constant.go index 881bbd073..414be38c3 100644 --- a/vendor/github.com/mgechev/revive/rule/add-constant.go +++ b/vendor/github.com/mgechev/revive/rule/add-constant.go @@ -2,10 +2,12 @@ package rule import ( "fmt" - "github.com/mgechev/revive/lint" "go/ast" "strconv" "strings" + "sync" + + "github.com/mgechev/revive/lint" ) const ( @@ -18,10 +20,10 @@ const ( type whiteList map[string]map[string]bool func newWhiteList() whiteList { - return map[string]map[string]bool{kindINT: map[string]bool{}, kindFLOAT: map[string]bool{}, kindSTRING: map[string]bool{}} + return map[string]map[string]bool{kindINT: {}, kindFLOAT: {}, kindSTRING: {}} } -func (wl whiteList) add(kind string, list string) { +func (wl whiteList) add(kind, list string) { elems := strings.Split(list, ",") for _, e := range elems { wl[kind][e] = true @@ -29,51 +31,15 @@ func (wl whiteList) add(kind string, list string) { } // AddConstantRule lints unused params in functions. -type AddConstantRule struct{} +type AddConstantRule struct { + whiteList whiteList + strLitLimit int + sync.Mutex +} // Apply applies the rule to given file. func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - strLitLimit := defaultStrLitLimit - var whiteList = newWhiteList() - if len(arguments) > 0 { - args, ok := arguments[0].(map[string]interface{}) - if !ok { - panic(fmt.Sprintf("Invalid argument to the add-constant rule. Expecting a k,v map, got %T", arguments[0])) - } - for k, v := range args { - kind := "" - switch k { - case "allowFloats": - kind = kindFLOAT - fallthrough - case "allowInts": - if kind == "" { - kind = kindINT - } - fallthrough - case "allowStrs": - if kind == "" { - kind = kindSTRING - } - list, ok := v.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v)) - } - whiteList.add(kind, list) - case "maxLitCount": - sl, ok := v.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v)) - } - - limit, err := strconv.Atoi(sl) - if err != nil { - panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v)) - } - strLitLimit = limit - } - } - } + r.configure(arguments) var failures []lint.Failure @@ -81,7 +47,7 @@ func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lin failures = append(failures, failure) } - w := lintAddConstantRule{onFailure: onFailure, strLits: make(map[string]int, 0), strLitLimit: strLitLimit, whiteLst: whiteList} + w := lintAddConstantRule{onFailure: onFailure, strLits: make(map[string]int), strLitLimit: r.strLitLimit, whiteLst: r.whiteList} ast.Walk(w, file.AST) @@ -89,7 +55,7 @@ func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lin } // Name returns the rule name. -func (r *AddConstantRule) Name() string { +func (*AddConstantRule) Name() string { return "add-constant" } @@ -114,7 +80,6 @@ func (w lintAddConstantRule) Visit(node ast.Node) ast.Visitor { } return w - } func (w lintAddConstantRule) checkStrLit(n *ast.BasicLit) { @@ -149,3 +114,52 @@ func (w lintAddConstantRule) checkNumLit(kind string, n *ast.BasicLit) { Failure: fmt.Sprintf("avoid magic numbers like '%s', create a named constant for it", n.Value), }) } + +func (r *AddConstantRule) configure(arguments lint.Arguments) { + r.Lock() + defer r.Unlock() + + if r.whiteList == nil { + r.strLitLimit = defaultStrLitLimit + r.whiteList = newWhiteList() + if len(arguments) > 0 { + args, ok := arguments[0].(map[string]interface{}) + if !ok { + panic(fmt.Sprintf("Invalid argument to the add-constant rule. Expecting a k,v map, got %T", arguments[0])) + } + for k, v := range args { + kind := "" + switch k { + case "allowFloats": + kind = kindFLOAT + fallthrough + case "allowInts": + if kind == "" { + kind = kindINT + } + fallthrough + case "allowStrs": + if kind == "" { + kind = kindSTRING + } + list, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v)) + } + r.whiteList.add(kind, list) + case "maxLitCount": + sl, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v)) + } + + limit, err := strconv.Atoi(sl) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v)) + } + r.strLitLimit = limit + } + } + } + } +} diff --git a/vendor/github.com/mgechev/revive/rule/argument-limit.go b/vendor/github.com/mgechev/revive/rule/argument-limit.go index 2b11d4982..8042da15e 100644 --- a/vendor/github.com/mgechev/revive/rule/argument-limit.go +++ b/vendor/github.com/mgechev/revive/rule/argument-limit.go @@ -3,31 +3,43 @@ package rule import ( "fmt" "go/ast" + "sync" "github.com/mgechev/revive/lint" ) // ArgumentsLimitRule lints given else constructs. -type ArgumentsLimitRule struct{} +type ArgumentsLimitRule struct { + total int + sync.Mutex +} -// Apply applies the rule to given file. -func (r *ArgumentsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - if len(arguments) != 1 { - panic(`invalid configuration for "argument-limit"`) - } +func (r *ArgumentsLimitRule) configure(arguments lint.Arguments) { + r.Lock() + if r.total == 0 { + checkNumberOfArguments(1, arguments, r.Name()) - total, ok := arguments[0].(int64) // Alt. non panicking version - if !ok { - panic(`invalid value passed as argument number to the "argument-list" rule`) + total, ok := arguments[0].(int64) // Alt. non panicking version + if !ok { + panic(`invalid value passed as argument number to the "argument-limit" rule`) + } + r.total = int(total) } + r.Unlock() +} + +// Apply applies the rule to given file. +func (r *ArgumentsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } walker := lintArgsNum{ - total: int(total), - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, + total: r.total, + onFailure: onFailure, } ast.Walk(walker, file.AST) @@ -36,7 +48,7 @@ func (r *ArgumentsLimitRule) Apply(file *lint.File, arguments lint.Arguments) [] } // Name returns the rule name. -func (r *ArgumentsLimitRule) Name() string { +func (*ArgumentsLimitRule) Name() string { return "argument-limit" } diff --git a/vendor/github.com/mgechev/revive/rule/atomic.go b/vendor/github.com/mgechev/revive/rule/atomic.go index 572e141da..287b28c21 100644 --- a/vendor/github.com/mgechev/revive/rule/atomic.go +++ b/vendor/github.com/mgechev/revive/rule/atomic.go @@ -12,10 +12,10 @@ import ( type AtomicRule struct{} // Apply applies the rule to given file. -func (r *AtomicRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*AtomicRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure walker := atomic{ - pkgTypesInfo: file.Pkg.TypesInfo, + pkgTypesInfo: file.Pkg.TypesInfo(), onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, @@ -27,7 +27,7 @@ func (r *AtomicRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { } // Name returns the rule name. -func (r *AtomicRule) Name() string { +func (*AtomicRule) Name() string { return "atomic" } diff --git a/vendor/github.com/mgechev/revive/rule/banned-characters.go b/vendor/github.com/mgechev/revive/rule/banned-characters.go new file mode 100644 index 000000000..76fa2235a --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/banned-characters.go @@ -0,0 +1,90 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// BannedCharsRule checks if a file contains banned characters. +type BannedCharsRule struct { + bannedCharList []string + sync.Mutex +} + +const bannedCharsRuleName = "banned-characters" + +func (r *BannedCharsRule) configure(arguments lint.Arguments) { + r.Lock() + if r.bannedCharList == nil { + checkNumberOfArguments(1, arguments, bannedCharsRuleName) + r.bannedCharList = r.getBannedCharsList(arguments) + } + r.Unlock() +} + +// Apply applied the rule to the given file. +func (r *BannedCharsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) + + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintBannedCharsRule{ + bannedChars: r.bannedCharList, + onFailure: onFailure, + } + + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name +func (*BannedCharsRule) Name() string { + return bannedCharsRuleName +} + +// getBannedCharsList converts arguments into the banned characters list +func (r *BannedCharsRule) getBannedCharsList(args lint.Arguments) []string { + var bannedChars []string + for _, char := range args { + charStr, ok := char.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument for the %s rule: expecting a string, got %T", r.Name(), char)) + } + bannedChars = append(bannedChars, charStr) + } + + return bannedChars +} + +type lintBannedCharsRule struct { + bannedChars []string + onFailure func(lint.Failure) +} + +// Visit checks for each node if an identifier contains banned characters +func (w lintBannedCharsRule) Visit(node ast.Node) ast.Visitor { + n, ok := node.(*ast.Ident) + if !ok { + return w + } + for _, c := range w.bannedChars { + ok := strings.Contains(n.Name, c) + if ok { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("banned character found: %s", c), + RuleName: bannedCharsRuleName, + Node: n, + }) + } + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/bare-return.go b/vendor/github.com/mgechev/revive/rule/bare-return.go index 3ee4c4adc..147fa84db 100644 --- a/vendor/github.com/mgechev/revive/rule/bare-return.go +++ b/vendor/github.com/mgechev/revive/rule/bare-return.go @@ -10,7 +10,7 @@ import ( type BareReturnRule struct{} // Apply applies the rule to given file. -func (r *BareReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*BareReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -23,7 +23,7 @@ func (r *BareReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure } // Name returns the rule name. -func (r *BareReturnRule) Name() string { +func (*BareReturnRule) Name() string { return "bare-return" } @@ -61,7 +61,7 @@ func (w bareReturnFinder) Visit(node ast.Node) ast.Visitor { _, ok := node.(*ast.FuncLit) if ok { // skip analysing function literals - // they will analyzed by the lintBareReturnRule.Visit method + // they will be analysed by the lintBareReturnRule.Visit method return nil } diff --git a/vendor/github.com/mgechev/revive/rule/blank-imports.go b/vendor/github.com/mgechev/revive/rule/blank-imports.go index 0a00e3707..a3d50b4f7 100644 --- a/vendor/github.com/mgechev/revive/rule/blank-imports.go +++ b/vendor/github.com/mgechev/revive/rule/blank-imports.go @@ -2,6 +2,7 @@ package rule import ( "go/ast" + "strings" "github.com/mgechev/revive/lint" ) @@ -9,66 +10,66 @@ import ( // BlankImportsRule lints given else constructs. type BlankImportsRule struct{} -// Apply applies the rule to given file. -func (r *BlankImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure - - fileAst := file.AST - walker := lintBlankImports{ - file: file, - fileAst: fileAst, - onFailure: func(failure lint.Failure) { - failures = append(failures, failure) - }, - } - - ast.Walk(walker, fileAst) - - return failures -} - // Name returns the rule name. -func (r *BlankImportsRule) Name() string { +func (*BlankImportsRule) Name() string { return "blank-imports" } -type lintBlankImports struct { - fileAst *ast.File - file *lint.File - onFailure func(lint.Failure) -} - -func (w lintBlankImports) Visit(_ ast.Node) ast.Visitor { - // In package main and in tests, we don't complain about blank imports. - if w.file.Pkg.IsMain() || w.file.IsTest() { +// Apply applies the rule to given file. +func (r *BlankImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + if file.Pkg.IsMain() || file.IsTest() { return nil } + const ( + message = "a blank import should be only in a main or test package, or have a comment justifying it" + category = "imports" + + embedImportPath = `"embed"` + ) + + var failures []lint.Failure + // The first element of each contiguous group of blank imports should have // an explanatory comment of some kind. - for i, imp := range w.fileAst.Imports { - pos := w.file.ToPosition(imp.Pos()) + for i, imp := range file.AST.Imports { + pos := file.ToPosition(imp.Pos()) if !isBlank(imp.Name) { continue // Ignore non-blank imports. } + if i > 0 { - prev := w.fileAst.Imports[i-1] - prevPos := w.file.ToPosition(prev.Pos()) - if isBlank(prev.Name) && prevPos.Line+1 == pos.Line { - continue // A subsequent blank in a group. + prev := file.AST.Imports[i-1] + prevPos := file.ToPosition(prev.Pos()) + + isSubsequentBlancInAGroup := prevPos.Line+1 == pos.Line && prev.Path.Value != embedImportPath && isBlank(prev.Name) + if isSubsequentBlancInAGroup { + continue } } + if imp.Path.Value == embedImportPath && r.fileHasValidEmbedComment(file.AST) { + continue + } + // This is the first blank import of a group. if imp.Doc == nil && imp.Comment == nil { - w.onFailure(lint.Failure{ - Node: imp, - Failure: "a blank import should be only in a main or test package, or have a comment justifying it", - Confidence: 1, - Category: "imports", - }) + failures = append(failures, lint.Failure{Failure: message, Category: category, Node: imp, Confidence: 1}) } } - return nil + + return failures +} + +func (*BlankImportsRule) fileHasValidEmbedComment(fileAst *ast.File) bool { + for _, commentGroup := range fileAst.Comments { + for _, comment := range commentGroup.List { + if strings.HasPrefix(comment.Text, "//go:embed ") { + return true + } + } + } + + return false } diff --git a/vendor/github.com/mgechev/revive/rule/bool-literal-in-expr.go b/vendor/github.com/mgechev/revive/rule/bool-literal-in-expr.go index 0a4e696c6..d6150339b 100644 --- a/vendor/github.com/mgechev/revive/rule/bool-literal-in-expr.go +++ b/vendor/github.com/mgechev/revive/rule/bool-literal-in-expr.go @@ -11,7 +11,7 @@ import ( type BoolLiteralRule struct{} // Apply applies the rule to given file. -func (r *BoolLiteralRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*BoolLiteralRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -26,7 +26,7 @@ func (r *BoolLiteralRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failur } // Name returns the rule name. -func (r *BoolLiteralRule) Name() string { +func (*BoolLiteralRule) Name() string { return "bool-literal-in-expr" } @@ -63,7 +63,7 @@ func (w *lintBoolLiteral) Visit(node ast.Node) ast.Visitor { return w } -func (w lintBoolLiteral) addFailure(node ast.Node, msg string, cat string) { +func (w lintBoolLiteral) addFailure(node ast.Node, msg, cat string) { w.onFailure(lint.Failure{ Confidence: 1, Node: node, diff --git a/vendor/github.com/mgechev/revive/rule/call-to-gc.go b/vendor/github.com/mgechev/revive/rule/call-to-gc.go index 06126611b..9c68380a4 100644 --- a/vendor/github.com/mgechev/revive/rule/call-to-gc.go +++ b/vendor/github.com/mgechev/revive/rule/call-to-gc.go @@ -10,14 +10,14 @@ import ( type CallToGCRule struct{} // Apply applies the rule to given file. -func (r *CallToGCRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*CallToGCRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) } - var gcTriggeringFunctions = map[string]map[string]bool{ - "runtime": map[string]bool{"GC": true}, + gcTriggeringFunctions := map[string]map[string]bool{ + "runtime": {"GC": true}, } w := lintCallToGC{onFailure, gcTriggeringFunctions} @@ -27,7 +27,7 @@ func (r *CallToGCRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { } // Name returns the rule name. -func (r *CallToGCRule) Name() string { +func (*CallToGCRule) Name() string { return "call-to-gc" } diff --git a/vendor/github.com/mgechev/revive/rule/cognitive-complexity.go b/vendor/github.com/mgechev/revive/rule/cognitive-complexity.go index ccd36bd09..a9c11a7d0 100644 --- a/vendor/github.com/mgechev/revive/rule/cognitive-complexity.go +++ b/vendor/github.com/mgechev/revive/rule/cognitive-complexity.go @@ -4,42 +4,53 @@ import ( "fmt" "go/ast" "go/token" + "sync" "github.com/mgechev/revive/lint" "golang.org/x/tools/go/ast/astutil" ) // CognitiveComplexityRule lints given else constructs. -type CognitiveComplexityRule struct{} +type CognitiveComplexityRule struct { + maxComplexity int + sync.Mutex +} + +func (r *CognitiveComplexityRule) configure(arguments lint.Arguments) { + r.Lock() + if r.maxComplexity == 0 { + checkNumberOfArguments(1, arguments, r.Name()) + + complexity, ok := arguments[0].(int64) + if !ok { + panic(fmt.Sprintf("invalid argument type for cognitive-complexity, expected int64, got %T", arguments[0])) + } + r.maxComplexity = int(complexity) + } + r.Unlock() +} // Apply applies the rule to given file. func (r *CognitiveComplexityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - var failures []lint.Failure + r.configure(arguments) - const expectedArgumentsCount = 1 - if len(arguments) < expectedArgumentsCount { - panic(fmt.Sprintf("not enough arguments for cognitive-complexity, expected %d, got %d", expectedArgumentsCount, len(arguments))) - } - complexity, ok := arguments[0].(int64) - if !ok { - panic(fmt.Sprintf("invalid argument type for cognitive-complexity, expected int64, got %T", arguments[0])) - } + var failures []lint.Failure linter := cognitiveComplexityLinter{ file: file, - maxComplexity: int(complexity), + maxComplexity: r.maxComplexity, onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, } - linter.lint() + linter.lintCognitiveComplexity() return failures } // Name returns the rule name. -func (r *CognitiveComplexityRule) Name() string { +func (*CognitiveComplexityRule) Name() string { return "cognitive-complexity" } @@ -49,7 +60,7 @@ type cognitiveComplexityLinter struct { onFailure func(lint.Failure) } -func (w cognitiveComplexityLinter) lint() { +func (w cognitiveComplexityLinter) lintCognitiveComplexity() { f := w.file for _, decl := range f.AST.Decls { if fn, ok := decl.(*ast.FuncDecl); ok && fn.Body != nil { diff --git a/vendor/github.com/mgechev/revive/rule/confusing-naming.go b/vendor/github.com/mgechev/revive/rule/confusing-naming.go index 143bb18c3..34cdb907a 100644 --- a/vendor/github.com/mgechev/revive/rule/confusing-naming.go +++ b/vendor/github.com/mgechev/revive/rule/confusing-naming.go @@ -3,7 +3,6 @@ package rule import ( "fmt" "go/ast" - "strings" "sync" @@ -49,7 +48,7 @@ var allPkgs = packages{pkgs: make([]pkgMethods, 1)} type ConfusingNamingRule struct{} // Apply applies the rule to given file. -func (r *ConfusingNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ConfusingNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST pkgm := allPkgs.methodNames(file.Pkg) @@ -67,11 +66,11 @@ func (r *ConfusingNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa } // Name returns the rule name. -func (r *ConfusingNamingRule) Name() string { +func (*ConfusingNamingRule) Name() string { return "confusing-naming" } -//checkMethodName checks if a given method/function name is similar (just case differences) to other method/function of the same struct/file. +// checkMethodName checks if a given method/function name is similar (just case differences) to other method/function of the same struct/file. func checkMethodName(holder string, id *ast.Ident, w *lintConfusingNames) { if id.Name == "init" && holder == defaultStructName { // ignore init functions @@ -128,7 +127,7 @@ type lintConfusingNames struct { const defaultStructName = "_" // used to map functions -//getStructName of a function receiver. Defaults to defaultStructName +// getStructName of a function receiver. Defaults to defaultStructName func getStructName(r *ast.FieldList) string { result := defaultStructName diff --git a/vendor/github.com/mgechev/revive/rule/confusing-results.go b/vendor/github.com/mgechev/revive/rule/confusing-results.go index 1d386b3db..1b79ada9c 100644 --- a/vendor/github.com/mgechev/revive/rule/confusing-results.go +++ b/vendor/github.com/mgechev/revive/rule/confusing-results.go @@ -10,7 +10,7 @@ import ( type ConfusingResultsRule struct{} // Apply applies the rule to given file. -func (r *ConfusingResultsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ConfusingResultsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -26,7 +26,7 @@ func (r *ConfusingResultsRule) Apply(file *lint.File, _ lint.Arguments) []lint.F } // Name returns the rule name. -func (r *ConfusingResultsRule) Name() string { +func (*ConfusingResultsRule) Name() string { return "confusing-results" } @@ -60,7 +60,6 @@ func (w lintConfusingResults) Visit(n ast.Node) ast.Visitor { break } lastType = t.Name - } return w diff --git a/vendor/github.com/mgechev/revive/rule/constant-logical-expr.go b/vendor/github.com/mgechev/revive/rule/constant-logical-expr.go index 6a9156111..9abc95d67 100644 --- a/vendor/github.com/mgechev/revive/rule/constant-logical-expr.go +++ b/vendor/github.com/mgechev/revive/rule/constant-logical-expr.go @@ -1,9 +1,10 @@ package rule import ( - "github.com/mgechev/revive/lint" "go/ast" "go/token" + + "github.com/mgechev/revive/lint" ) // ConstantLogicalExprRule warns on constant logical expressions. @@ -24,7 +25,7 @@ func (r *ConstantLogicalExprRule) Apply(file *lint.File, _ lint.Arguments) []lin } // Name returns the rule name. -func (r *ConstantLogicalExprRule) Name() string { +func (*ConstantLogicalExprRule) Name() string { return "constant-logical-expr" } @@ -44,11 +45,13 @@ func (w *lintConstantLogicalExpr) Visit(node ast.Node) ast.Visitor { return w } - if n.Op == token.EQL { + // Handles cases like: a <= a, a == a, a >= a + if w.isEqualityOperator(n.Op) { w.newFailure(n, "expression always evaluates to true") return w } + // Handles cases like: a < a, a > a, a != a if w.isInequalityOperator(n.Op) { w.newFailure(n, "expression always evaluates to false") return w @@ -69,9 +72,18 @@ func (w *lintConstantLogicalExpr) isOperatorWithLogicalResult(t token.Token) boo return false } +func (w *lintConstantLogicalExpr) isEqualityOperator(t token.Token) bool { + switch t { + case token.EQL, token.LEQ, token.GEQ: + return true + } + + return false +} + func (w *lintConstantLogicalExpr) isInequalityOperator(t token.Token) bool { switch t { - case token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ: + case token.LSS, token.GTR, token.NEQ: return true } diff --git a/vendor/github.com/mgechev/revive/rule/context-as-argument.go b/vendor/github.com/mgechev/revive/rule/context-as-argument.go index 0ed28a82a..3c400065e 100644 --- a/vendor/github.com/mgechev/revive/rule/context-as-argument.go +++ b/vendor/github.com/mgechev/revive/rule/context-as-argument.go @@ -1,41 +1,51 @@ package rule import ( + "fmt" "go/ast" + "strings" + "sync" "github.com/mgechev/revive/lint" ) // ContextAsArgumentRule lints given else constructs. -type ContextAsArgumentRule struct{} +type ContextAsArgumentRule struct { + allowTypesLUT map[string]struct{} + sync.Mutex +} // Apply applies the rule to given file. -func (r *ContextAsArgumentRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure +func (r *ContextAsArgumentRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.Lock() + if r.allowTypesLUT == nil { + r.allowTypesLUT = getAllowTypesFromArguments(args) + } + r.Unlock() - fileAst := file.AST + var failures []lint.Failure + r.Lock() walker := lintContextArguments{ - file: file, - fileAst: fileAst, + allowTypesLUT: r.allowTypesLUT, onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, } + r.Unlock() - ast.Walk(walker, fileAst) + ast.Walk(walker, file.AST) return failures } // Name returns the rule name. -func (r *ContextAsArgumentRule) Name() string { +func (*ContextAsArgumentRule) Name() string { return "context-as-argument" } type lintContextArguments struct { - file *lint.File - fileAst *ast.File - onFailure func(lint.Failure) + allowTypesLUT map[string]struct{} + onFailure func(lint.Failure) } func (w lintContextArguments) Visit(n ast.Node) ast.Visitor { @@ -43,18 +53,58 @@ func (w lintContextArguments) Visit(n ast.Node) ast.Visitor { if !ok || len(fn.Type.Params.List) <= 1 { return w } + + fnArgs := fn.Type.Params.List + // A context.Context should be the first parameter of a function. // Flag any that show up after the first. - for _, arg := range fn.Type.Params.List[1:] { - if isPkgDot(arg.Type, "context", "Context") { + isCtxStillAllowed := true + for _, arg := range fnArgs { + argIsCtx := isPkgDot(arg.Type, "context", "Context") + if argIsCtx && !isCtxStillAllowed { w.onFailure(lint.Failure{ - Node: fn, + Node: arg, Category: "arg-order", Failure: "context.Context should be the first parameter of a function", Confidence: 0.9, }) break // only flag one } + + typeName := gofmt(arg.Type) + // a parameter of type context.Context is still allowed if the current arg type is in the LUT + _, isCtxStillAllowed = w.allowTypesLUT[typeName] + } + + return nil // avoid visiting the function body +} + +func getAllowTypesFromArguments(args lint.Arguments) map[string]struct{} { + allowTypesBefore := []string{} + if len(args) >= 1 { + argKV, ok := args[0].(map[string]interface{}) + if !ok { + panic(fmt.Sprintf("Invalid argument to the context-as-argument rule. Expecting a k,v map, got %T", args[0])) + } + for k, v := range argKV { + switch k { + case "allowTypesBefore": + typesBefore, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the context-as-argument.allowTypesBefore rule. Expecting a string, got %T", v)) + } + allowTypesBefore = append(allowTypesBefore, strings.Split(typesBefore, ",")...) + default: + panic(fmt.Sprintf("Invalid argument to the context-as-argument rule. Unrecognized key %s", k)) + } + } + } + + result := make(map[string]struct{}, len(allowTypesBefore)) + for _, v := range allowTypesBefore { + result[v] = struct{}{} } - return w + + result["context.Context"] = struct{}{} // context.Context is always allowed before another context.Context + return result } diff --git a/vendor/github.com/mgechev/revive/rule/context-keys-type.go b/vendor/github.com/mgechev/revive/rule/context-keys-type.go index 9c2f0bbd7..60ccec560 100644 --- a/vendor/github.com/mgechev/revive/rule/context-keys-type.go +++ b/vendor/github.com/mgechev/revive/rule/context-keys-type.go @@ -12,7 +12,7 @@ import ( type ContextKeysType struct{} // Apply applies the rule to given file. -func (r *ContextKeysType) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ContextKeysType) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -31,7 +31,7 @@ func (r *ContextKeysType) Apply(file *lint.File, _ lint.Arguments) []lint.Failur } // Name returns the rule name. -func (r *ContextKeysType) Name() string { +func (*ContextKeysType) Name() string { return "context-keys-type" } @@ -68,7 +68,7 @@ func checkContextKeyType(w lintContextKeyTypes, x *ast.CallExpr) { if len(x.Args) != 3 { return } - key := f.Pkg.TypesInfo.Types[x.Args[1]] + key := f.Pkg.TypesInfo().Types[x.Args[1]] if ktyp, ok := key.Type.(*types.Basic); ok && ktyp.Kind() != types.Invalid { w.onFailure(lint.Failure{ diff --git a/vendor/github.com/mgechev/revive/rule/cyclomatic.go b/vendor/github.com/mgechev/revive/rule/cyclomatic.go index 48ea80a6a..afd41818b 100644 --- a/vendor/github.com/mgechev/revive/rule/cyclomatic.go +++ b/vendor/github.com/mgechev/revive/rule/cyclomatic.go @@ -4,6 +4,7 @@ import ( "fmt" "go/ast" "go/token" + "sync" "github.com/mgechev/revive/lint" ) @@ -11,21 +12,35 @@ import ( // Based on https://github.com/fzipp/gocyclo // CyclomaticRule lints given else constructs. -type CyclomaticRule struct{} +type CyclomaticRule struct { + maxComplexity int + sync.Mutex +} -// Apply applies the rule to given file. -func (r *CyclomaticRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - var failures []lint.Failure +func (r *CyclomaticRule) configure(arguments lint.Arguments) { + r.Lock() + if r.maxComplexity == 0 { + checkNumberOfArguments(1, arguments, r.Name()) - complexity, ok := arguments[0].(int64) // Alt. non panicking version - if !ok { - panic("invalid argument for cyclomatic complexity") + complexity, ok := arguments[0].(int64) // Alt. non panicking version + if !ok { + panic(fmt.Sprintf("invalid argument for cyclomatic complexity; expected int but got %T", arguments[0])) + } + r.maxComplexity = int(complexity) } + r.Unlock() +} +// Apply applies the rule to given file. +func (r *CyclomaticRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) + + var failures []lint.Failure fileAst := file.AST + walker := lintCyclomatic{ file: file, - complexity: int(complexity), + complexity: r.maxComplexity, onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, @@ -37,7 +52,7 @@ func (r *CyclomaticRule) Apply(file *lint.File, arguments lint.Arguments) []lint } // Name returns the rule name. -func (r *CyclomaticRule) Name() string { +func (*CyclomaticRule) Name() string { return "cyclomatic" } @@ -56,8 +71,9 @@ func (w lintCyclomatic) Visit(_ ast.Node) ast.Visitor { w.onFailure(lint.Failure{ Confidence: 1, Category: "maintenance", - Failure: fmt.Sprintf("function %s has cyclomatic complexity %d", funcName(fn), c), - Node: fn, + Failure: fmt.Sprintf("function %s has cyclomatic complexity %d (> max enabled %d)", + funcName(fn), c, w.complexity), + Node: fn, }) } } diff --git a/vendor/github.com/mgechev/revive/rule/datarace.go b/vendor/github.com/mgechev/revive/rule/datarace.go new file mode 100644 index 000000000..26fcadcdc --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/datarace.go @@ -0,0 +1,142 @@ +package rule + +import ( + "fmt" + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// DataRaceRule lints assignments to value method-receivers. +type DataRaceRule struct{} + +// Apply applies the rule to given file. +func (*DataRaceRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + w := lintDataRaces{onFailure: onFailure} + + ast.Walk(w, file.AST) + + return failures +} + +// Name returns the rule name. +func (*DataRaceRule) Name() string { + return "datarace" +} + +type lintDataRaces struct { + onFailure func(failure lint.Failure) +} + +func (w lintDataRaces) Visit(n ast.Node) ast.Visitor { + node, ok := n.(*ast.FuncDecl) + if !ok { + return w // not function declaration + } + if node.Body == nil { + return nil // empty body + } + + results := node.Type.Results + + returnIDs := map[*ast.Object]struct{}{} + if results != nil { + returnIDs = w.ExtractReturnIDs(results.List) + } + fl := &lintFunctionForDataRaces{onFailure: w.onFailure, returnIDs: returnIDs, rangeIDs: map[*ast.Object]struct{}{}} + ast.Walk(fl, node.Body) + + return nil +} + +func (w lintDataRaces) ExtractReturnIDs(fields []*ast.Field) map[*ast.Object]struct{} { + r := map[*ast.Object]struct{}{} + for _, f := range fields { + for _, id := range f.Names { + r[id.Obj] = struct{}{} + } + } + + return r +} + +type lintFunctionForDataRaces struct { + _ struct{} + onFailure func(failure lint.Failure) + returnIDs map[*ast.Object]struct{} + rangeIDs map[*ast.Object]struct{} +} + +func (w lintFunctionForDataRaces) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.RangeStmt: + if n.Body == nil { + return nil + } + + getIds := func(exprs ...ast.Expr) []*ast.Ident { + r := []*ast.Ident{} + for _, expr := range exprs { + if id, ok := expr.(*ast.Ident); ok { + r = append(r, id) + } + } + return r + } + + ids := getIds(n.Key, n.Value) + for _, id := range ids { + w.rangeIDs[id.Obj] = struct{}{} + } + + ast.Walk(w, n.Body) + + for _, id := range ids { + delete(w.rangeIDs, id.Obj) + } + + return nil // do not visit the body of the range, it has been already visited + case *ast.GoStmt: + f := n.Call.Fun + funcLit, ok := f.(*ast.FuncLit) + if !ok { + return nil + } + selectIDs := func(n ast.Node) bool { + _, ok := n.(*ast.Ident) + return ok + } + + ids := pick(funcLit.Body, selectIDs, nil) + for _, id := range ids { + id := id.(*ast.Ident) + _, isRangeID := w.rangeIDs[id.Obj] + _, isReturnID := w.returnIDs[id.Obj] + + switch { + case isRangeID: + w.onFailure(lint.Failure{ + Confidence: 1, + Node: id, + Category: "logic", + Failure: fmt.Sprintf("datarace: range value %s is captured (by-reference) in goroutine", id.Name), + }) + case isReturnID: + w.onFailure(lint.Failure{ + Confidence: 0.8, + Node: id, + Category: "logic", + Failure: fmt.Sprintf("potential datarace: return value %s is captured (by-reference) in goroutine", id.Name), + }) + } + } + + return nil + } + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/deep-exit.go b/vendor/github.com/mgechev/revive/rule/deep-exit.go index f49e93dd4..918d4294a 100644 --- a/vendor/github.com/mgechev/revive/rule/deep-exit.go +++ b/vendor/github.com/mgechev/revive/rule/deep-exit.go @@ -11,16 +11,16 @@ import ( type DeepExitRule struct{} // Apply applies the rule to given file. -func (r *DeepExitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*DeepExitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) } - var exitFunctions = map[string]map[string]bool{ - "os": map[string]bool{"Exit": true}, - "syscall": map[string]bool{"Exit": true}, - "log": map[string]bool{ + exitFunctions := map[string]map[string]bool{ + "os": {"Exit": true}, + "syscall": {"Exit": true}, + "log": { "Fatal": true, "Fatalf": true, "Fatalln": true, @@ -36,7 +36,7 @@ func (r *DeepExitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { } // Name returns the rule name. -func (r *DeepExitRule) Name() string { +func (*DeepExitRule) Name() string { return "deep-exit" } diff --git a/vendor/github.com/mgechev/revive/rule/defer.go b/vendor/github.com/mgechev/revive/rule/defer.go index 2ec7ef47c..f8224fd4d 100644 --- a/vendor/github.com/mgechev/revive/rule/defer.go +++ b/vendor/github.com/mgechev/revive/rule/defer.go @@ -3,23 +3,34 @@ package rule import ( "fmt" "go/ast" + "sync" "github.com/mgechev/revive/lint" ) // DeferRule lints unused params in functions. -type DeferRule struct{} +type DeferRule struct { + allow map[string]bool + sync.Mutex +} + +func (r *DeferRule) configure(arguments lint.Arguments) { + r.Lock() + if r.allow == nil { + r.allow = r.allowFromArgs(arguments) + } + r.Unlock() +} // Apply applies the rule to given file. func (r *DeferRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - allow := r.allowFromArgs(arguments) + r.configure(arguments) var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) } - - w := lintDeferRule{onFailure: onFailure, allow: allow} + w := lintDeferRule{onFailure: onFailure, allow: r.allow} ast.Walk(w, file.AST) @@ -27,18 +38,19 @@ func (r *DeferRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Fail } // Name returns the rule name. -func (r *DeferRule) Name() string { +func (*DeferRule) Name() string { return "defer" } -func (r *DeferRule) allowFromArgs(args lint.Arguments) map[string]bool { +func (*DeferRule) allowFromArgs(args lint.Arguments) map[string]bool { if len(args) < 1 { allow := map[string]bool{ - "loop": true, - "call-chain": true, - "method-call": true, - "return": true, - "recover": true, + "loop": true, + "call-chain": true, + "method-call": true, + "return": true, + "recover": true, + "immediate-recover": true, } return allow @@ -85,12 +97,30 @@ func (w lintDeferRule) Visit(node ast.Node) ast.Visitor { w.newFailure("return in a defer function has no effect", n, 1.0, "logic", "return") } case *ast.CallExpr: - if isIdent(n.Fun, "recover") && !w.inADefer { + if !w.inADefer && isIdent(n.Fun, "recover") { + // func fn() { recover() } + // // confidence is not 1 because recover can be in a function that is deferred elsewhere w.newFailure("recover must be called inside a deferred function", n, 0.8, "logic", "recover") + } else if w.inADefer && !w.inAFuncLit && isIdent(n.Fun, "recover") { + // defer helper(recover()) + // + // confidence is not truly 1 because this could be in a correctly-deferred func, + // but it is very likely to be a misunderstanding of defer's behavior around arguments. + w.newFailure("recover must be called inside a deferred function, this is executing recover immediately", n, 1, "logic", "immediate-recover") } case *ast.DeferStmt: + if isIdent(n.Call.Fun, "recover") { + // defer recover() + // + // confidence is not truly 1 because this could be in a correctly-deferred func, + // but normally this doesn't suppress a panic, and even if it did it would silently discard the value. + w.newFailure("recover must be called inside a deferred function, this is executing recover immediately", n, 1, "logic", "immediate-recover") + } w.visitSubtree(n.Call.Fun, true, false, false) + for _, a := range n.Call.Args { + w.visitSubtree(a, true, false, false) // check arguments, they should not contain recover() + } if w.inALoop { w.newFailure("prefer not to defer inside loops", n, 1.0, "bad practice", "loop") @@ -114,16 +144,17 @@ func (w lintDeferRule) Visit(node ast.Node) ast.Visitor { } func (w lintDeferRule) visitSubtree(n ast.Node, inADefer, inALoop, inAFuncLit bool) { - nw := &lintDeferRule{ + nw := lintDeferRule{ onFailure: w.onFailure, inADefer: inADefer, inALoop: inALoop, inAFuncLit: inAFuncLit, - allow: w.allow} + allow: w.allow, + } ast.Walk(nw, n) } -func (w lintDeferRule) newFailure(msg string, node ast.Node, confidence float64, cat string, subcase string) { +func (w lintDeferRule) newFailure(msg string, node ast.Node, confidence float64, cat, subcase string) { if !w.allow[subcase] { return } diff --git a/vendor/github.com/mgechev/revive/rule/dot-imports.go b/vendor/github.com/mgechev/revive/rule/dot-imports.go index 78419d7d6..25ff526cb 100644 --- a/vendor/github.com/mgechev/revive/rule/dot-imports.go +++ b/vendor/github.com/mgechev/revive/rule/dot-imports.go @@ -10,7 +10,7 @@ import ( type DotImportsRule struct{} // Apply applies the rule to given file. -func (r *DotImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*DotImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -28,7 +28,7 @@ func (r *DotImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure } // Name returns the rule name. -func (r *DotImportsRule) Name() string { +func (*DotImportsRule) Name() string { return "dot-imports" } diff --git a/vendor/github.com/mgechev/revive/rule/duplicated-imports.go b/vendor/github.com/mgechev/revive/rule/duplicated-imports.go index 485b6a2ea..2b177fac6 100644 --- a/vendor/github.com/mgechev/revive/rule/duplicated-imports.go +++ b/vendor/github.com/mgechev/revive/rule/duplicated-imports.go @@ -10,7 +10,7 @@ import ( type DuplicatedImportsRule struct{} // Apply applies the rule to given file. -func (r *DuplicatedImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*DuplicatedImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure impPaths := map[string]struct{}{} @@ -34,6 +34,6 @@ func (r *DuplicatedImportsRule) Apply(file *lint.File, _ lint.Arguments) []lint. } // Name returns the rule name. -func (r *DuplicatedImportsRule) Name() string { +func (*DuplicatedImportsRule) Name() string { return "duplicated-imports" } diff --git a/vendor/github.com/mgechev/revive/rule/early-return.go b/vendor/github.com/mgechev/revive/rule/early-return.go index ffb568a86..bfbf6717c 100644 --- a/vendor/github.com/mgechev/revive/rule/early-return.go +++ b/vendor/github.com/mgechev/revive/rule/early-return.go @@ -10,7 +10,7 @@ import ( type EarlyReturnRule struct{} // Apply applies the rule to given file. -func (r *EarlyReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*EarlyReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -23,7 +23,7 @@ func (r *EarlyReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failur } // Name returns the rule name. -func (r *EarlyReturnRule) Name() string { +func (*EarlyReturnRule) Name() string { return "early-return" } diff --git a/vendor/github.com/mgechev/revive/rule/empty-block.go b/vendor/github.com/mgechev/revive/rule/empty-block.go index fbec4d93c..8a4a0fef1 100644 --- a/vendor/github.com/mgechev/revive/rule/empty-block.go +++ b/vendor/github.com/mgechev/revive/rule/empty-block.go @@ -10,20 +10,20 @@ import ( type EmptyBlockRule struct{} // Apply applies the rule to given file. -func (r *EmptyBlockRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*EmptyBlockRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) } - w := lintEmptyBlock{make(map[*ast.BlockStmt]bool, 0), onFailure} + w := lintEmptyBlock{make(map[*ast.BlockStmt]bool), onFailure} ast.Walk(w, file.AST) return failures } // Name returns the rule name. -func (r *EmptyBlockRule) Name() string { +func (*EmptyBlockRule) Name() string { return "empty-block" } diff --git a/vendor/github.com/mgechev/revive/rule/empty-lines.go b/vendor/github.com/mgechev/revive/rule/empty-lines.go index 61d9281bf..12866072e 100644 --- a/vendor/github.com/mgechev/revive/rule/empty-lines.go +++ b/vendor/github.com/mgechev/revive/rule/empty-lines.go @@ -18,25 +18,25 @@ func (r *EmptyLinesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure failures = append(failures, failure) } - w := lintEmptyLines{file, file.CommentMap(), onFailure} + w := lintEmptyLines{file, r.commentLines(file.CommentMap(), file), onFailure} ast.Walk(w, file.AST) return failures } // Name returns the rule name. -func (r *EmptyLinesRule) Name() string { +func (*EmptyLinesRule) Name() string { return "empty-lines" } type lintEmptyLines struct { file *lint.File - cmap ast.CommentMap + cmap map[int]struct{} onFailure func(lint.Failure) } func (w lintEmptyLines) Visit(node ast.Node) ast.Visitor { block, ok := node.(*ast.BlockStmt) - if !ok { + if !ok || len(block.List) == 0 { return w } @@ -47,67 +47,59 @@ func (w lintEmptyLines) Visit(node ast.Node) ast.Visitor { } func (w lintEmptyLines) checkStart(block *ast.BlockStmt) { - if len(block.List) == 0 { - return - } - - start := w.position(block.Lbrace) + blockStart := w.position(block.Lbrace) firstNode := block.List[0] + firstStmt := w.position(firstNode.Pos()) - if w.commentBetween(start, firstNode) { + firstBlockLineIsStmt := firstStmt.Line-(blockStart.Line+1) == 0 + _, firstBlockLineIsComment := w.cmap[blockStart.Line+1] + if firstBlockLineIsStmt || firstBlockLineIsComment { return } - first := w.position(firstNode.Pos()) - if first.Line-start.Line > 1 { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: block, - Category: "style", - Failure: "extra empty line at the start of a block", - }) - } + w.onFailure(lint.Failure{ + Confidence: 1, + Node: block, + Category: "style", + Failure: "extra empty line at the start of a block", + }) } func (w lintEmptyLines) checkEnd(block *ast.BlockStmt) { - if len(block.List) < 1 { - return - } - - end := w.position(block.Rbrace) + blockEnd := w.position(block.Rbrace) lastNode := block.List[len(block.List)-1] + lastStmt := w.position(lastNode.End()) - if w.commentBetween(end, lastNode) { + lastBlockLineIsStmt := (blockEnd.Line-1)-lastStmt.Line == 0 + _, lastBlockLineIsComment := w.cmap[blockEnd.Line-1] + if lastBlockLineIsStmt || lastBlockLineIsComment { return } - last := w.position(lastNode.End()) - if end.Line-last.Line > 1 { - w.onFailure(lint.Failure{ - Confidence: 1, - Node: lastNode, - Category: "style", - Failure: "extra empty line at the end of a block", - }) - } + w.onFailure(lint.Failure{ + Confidence: 1, + Node: block, + Category: "style", + Failure: "extra empty line at the end of a block", + }) } -func (w lintEmptyLines) commentBetween(position token.Position, node ast.Node) bool { - comments := w.cmap.Filter(node).Comments() - if len(comments) == 0 { - return false - } +func (w lintEmptyLines) position(pos token.Pos) token.Position { + return w.file.ToPosition(pos) +} + +func (*EmptyLinesRule) commentLines(cmap ast.CommentMap, file *lint.File) map[int]struct{} { + result := map[int]struct{}{} - for _, comment := range comments { - start, end := w.position(comment.Pos()), w.position(comment.End()) - if start.Line-position.Line == 1 || position.Line-end.Line == 1 { - return true + for _, comments := range cmap { + for _, comment := range comments { + start := file.ToPosition(comment.Pos()) + end := file.ToPosition(comment.End()) + for i := start.Line; i <= end.Line; i++ { + result[i] = struct{}{} + } } } - return false -} - -func (w lintEmptyLines) position(pos token.Pos) token.Position { - return w.file.ToPosition(pos) + return result } diff --git a/vendor/github.com/mgechev/revive/rule/error-naming.go b/vendor/github.com/mgechev/revive/rule/error-naming.go index 3a1080625..a4f24f3f0 100644 --- a/vendor/github.com/mgechev/revive/rule/error-naming.go +++ b/vendor/github.com/mgechev/revive/rule/error-naming.go @@ -13,7 +13,7 @@ import ( type ErrorNamingRule struct{} // Apply applies the rule to given file. -func (r *ErrorNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ErrorNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -31,7 +31,7 @@ func (r *ErrorNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failur } // Name returns the rule name. -func (r *ErrorNamingRule) Name() string { +func (*ErrorNamingRule) Name() string { return "error-naming" } diff --git a/vendor/github.com/mgechev/revive/rule/error-return.go b/vendor/github.com/mgechev/revive/rule/error-return.go index 737d8c66f..a724e001c 100644 --- a/vendor/github.com/mgechev/revive/rule/error-return.go +++ b/vendor/github.com/mgechev/revive/rule/error-return.go @@ -10,7 +10,7 @@ import ( type ErrorReturnRule struct{} // Apply applies the rule to given file. -func (r *ErrorReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ErrorReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -28,7 +28,7 @@ func (r *ErrorReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failur } // Name returns the rule name. -func (r *ErrorReturnRule) Name() string { +func (*ErrorReturnRule) Name() string { return "error-return" } diff --git a/vendor/github.com/mgechev/revive/rule/error-strings.go b/vendor/github.com/mgechev/revive/rule/error-strings.go index b8a5b7ed7..81ebda540 100644 --- a/vendor/github.com/mgechev/revive/rule/error-strings.go +++ b/vendor/github.com/mgechev/revive/rule/error-strings.go @@ -4,6 +4,8 @@ import ( "go/ast" "go/token" "strconv" + "strings" + "sync" "unicode" "unicode/utf8" @@ -11,16 +13,60 @@ import ( ) // ErrorStringsRule lints given else constructs. -type ErrorStringsRule struct{} +type ErrorStringsRule struct { + errorFunctions map[string]map[string]struct{} + sync.Mutex +} + +func (r *ErrorStringsRule) configure(arguments lint.Arguments) { + r.Lock() + defer r.Unlock() + + if r.errorFunctions != nil { + return + } + + r.errorFunctions = map[string]map[string]struct{}{ + "fmt": { + "Errorf": {}, + }, + "errors": { + "Errorf": {}, + "WithMessage": {}, + "Wrap": {}, + "New": {}, + "WithMessagef": {}, + "Wrapf": {}, + }, + } + + var invalidCustomFunctions []string + for _, argument := range arguments { + if functionName, ok := argument.(string); ok { + fields := strings.Split(strings.TrimSpace(functionName), ".") + if len(fields) != 2 || len(fields[0]) == 0 || len(fields[1]) == 0 { + invalidCustomFunctions = append(invalidCustomFunctions, functionName) + continue + } + r.errorFunctions[fields[0]] = map[string]struct{}{fields[1]: {}} + } + } + if len(invalidCustomFunctions) != 0 { + panic("found invalid custom function: " + strings.Join(invalidCustomFunctions, ",")) + } +} // Apply applies the rule to given file. -func (r *ErrorStringsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (r *ErrorStringsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { var failures []lint.Failure + r.configure(arguments) + fileAst := file.AST walker := lintErrorStrings{ - file: file, - fileAst: fileAst, + file: file, + fileAst: fileAst, + errorFunctions: r.errorFunctions, onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, @@ -32,29 +78,36 @@ func (r *ErrorStringsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failu } // Name returns the rule name. -func (r *ErrorStringsRule) Name() string { +func (*ErrorStringsRule) Name() string { return "error-strings" } type lintErrorStrings struct { - file *lint.File - fileAst *ast.File - onFailure func(lint.Failure) + file *lint.File + fileAst *ast.File + errorFunctions map[string]map[string]struct{} + onFailure func(lint.Failure) } +// Visit browses the AST func (w lintErrorStrings) Visit(n ast.Node) ast.Visitor { ce, ok := n.(*ast.CallExpr) if !ok { return w } - if !isPkgDot(ce.Fun, "errors", "New") && !isPkgDot(ce.Fun, "fmt", "Errorf") { + + if len(ce.Args) < 1 { return w } - if len(ce.Args) < 1 { + + // expression matches the known pkg.function + ok = w.match(ce) + if !ok { return w } - str, ok := ce.Args[0].(*ast.BasicLit) - if !ok || str.Kind != token.STRING { + + str, ok := w.getMessage(ce) + if !ok { return w } s, _ := strconv.Unquote(str.Value) // can assume well-formed Go @@ -65,7 +118,6 @@ func (w lintErrorStrings) Visit(n ast.Node) ast.Visitor { if clean { return w } - w.onFailure(lint.Failure{ Node: str, Confidence: conf, @@ -75,6 +127,55 @@ func (w lintErrorStrings) Visit(n ast.Node) ast.Visitor { return w } +// match returns true if the expression corresponds to the known pkg.function +// i.e.: errors.Wrap +func (w lintErrorStrings) match(expr *ast.CallExpr) bool { + sel, ok := expr.Fun.(*ast.SelectorExpr) + if !ok { + return false + } + // retrieve the package + id, ok := sel.X.(*ast.Ident) + if !ok { + return false + } + functions, ok := w.errorFunctions[id.Name] + if !ok { + return false + } + // retrieve the function + _, ok = functions[sel.Sel.Name] + return ok +} + +// getMessage returns the message depending on its position +// returns false if the cast is unsuccessful +func (w lintErrorStrings) getMessage(expr *ast.CallExpr) (s *ast.BasicLit, success bool) { + str, ok := w.checkArg(expr, 0) + if ok { + return str, true + } + if len(expr.Args) < 2 { + return s, false + } + str, ok = w.checkArg(expr, 1) + if !ok { + return s, false + } + return str, true +} + +func (lintErrorStrings) checkArg(expr *ast.CallExpr, arg int) (s *ast.BasicLit, success bool) { + str, ok := expr.Args[arg].(*ast.BasicLit) + if !ok { + return s, false + } + if str.Kind != token.STRING { + return s, false + } + return str, true +} + func lintErrorString(s string) (isClean bool, conf float64) { const basicConfidence = 0.8 const capConfidence = basicConfidence - 0.2 diff --git a/vendor/github.com/mgechev/revive/rule/errorf.go b/vendor/github.com/mgechev/revive/rule/errorf.go index 1bffbab5b..1588a745d 100644 --- a/vendor/github.com/mgechev/revive/rule/errorf.go +++ b/vendor/github.com/mgechev/revive/rule/errorf.go @@ -13,7 +13,7 @@ import ( type ErrorfRule struct{} // Apply applies the rule to given file. -func (r *ErrorfRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ErrorfRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -32,7 +32,7 @@ func (r *ErrorfRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { } // Name returns the rule name. -func (r *ErrorfRule) Name() string { +func (*ErrorfRule) Name() string { return "errorf" } diff --git a/vendor/github.com/mgechev/revive/rule/exported.go b/vendor/github.com/mgechev/revive/rule/exported.go index b68f2bacc..b8663c48c 100644 --- a/vendor/github.com/mgechev/revive/rule/exported.go +++ b/vendor/github.com/mgechev/revive/rule/exported.go @@ -5,24 +5,49 @@ import ( "go/ast" "go/token" "strings" + "sync" "unicode" "unicode/utf8" + "github.com/mgechev/revive/internal/typeparams" "github.com/mgechev/revive/lint" ) // ExportedRule lints given else constructs. -type ExportedRule struct{} +type ExportedRule struct { + configured bool + checkPrivateReceivers bool + disableStutteringCheck bool + stuttersMsg string + sync.Mutex +} + +func (r *ExportedRule) configure(arguments lint.Arguments) { + r.Lock() + if !r.configured { + var sayRepetitiveInsteadOfStutters bool + r.checkPrivateReceivers, r.disableStutteringCheck, sayRepetitiveInsteadOfStutters = r.getConf(arguments) + r.stuttersMsg = "stutters" + if sayRepetitiveInsteadOfStutters { + r.stuttersMsg = "is repetitive" + } + + r.configured = true + } + r.Unlock() +} // Apply applies the rule to given file. -func (r *ExportedRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { - var failures []lint.Failure +func (r *ExportedRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configure(args) - if isTest(file) { + var failures []lint.Failure + if file.IsTest() { return failures } fileAst := file.AST + walker := lintExported{ file: file, fileAst: fileAst, @@ -30,6 +55,9 @@ func (r *ExportedRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { failures = append(failures, failure) }, genDeclMissingComments: make(map[*ast.GenDecl]bool), + checkPrivateReceivers: r.checkPrivateReceivers, + disableStutteringCheck: r.disableStutteringCheck, + stuttersMsg: r.stuttersMsg, } ast.Walk(&walker, fileAst) @@ -38,16 +66,45 @@ func (r *ExportedRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { } // Name returns the rule name. -func (r *ExportedRule) Name() string { +func (*ExportedRule) Name() string { return "exported" } +func (r *ExportedRule) getConf(args lint.Arguments) (checkPrivateReceivers, disableStutteringCheck, sayRepetitiveInsteadOfStutters bool) { + // if any, we expect a slice of strings as configuration + if len(args) < 1 { + return + } + for _, flag := range args { + flagStr, ok := flag.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument for the %s rule: expecting a string, got %T", r.Name(), flag)) + } + + switch flagStr { + case "checkPrivateReceivers": + checkPrivateReceivers = true + case "disableStutteringCheck": + disableStutteringCheck = true + case "sayRepetitiveInsteadOfStutters": + sayRepetitiveInsteadOfStutters = true + default: + panic(fmt.Sprintf("Unknown configuration flag %s for %s rule", flagStr, r.Name())) + } + } + + return +} + type lintExported struct { file *lint.File fileAst *ast.File lastGen *ast.GenDecl genDeclMissingComments map[*ast.GenDecl]bool onFailure func(lint.Failure) + checkPrivateReceivers bool + disableStutteringCheck bool + stuttersMsg string } func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) { @@ -60,8 +117,8 @@ func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) { if fn.Recv != nil && len(fn.Recv.List) > 0 { // method kind = "method" - recv := receiverType(fn) - if !ast.IsExported(recv) { + recv := typeparams.ReceiverType(fn) + if !w.checkPrivateReceivers && !ast.IsExported(recv) { // receiver is unexported return } @@ -70,7 +127,8 @@ func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) { } switch name { case "Len", "Less", "Swap": - if w.file.Pkg.Sortable[recv] { + sortables := w.file.Pkg.Sortable() + if sortables[recv] { return } } @@ -98,6 +156,10 @@ func (w *lintExported) lintFuncDoc(fn *ast.FuncDecl) { } func (w *lintExported) checkStutter(id *ast.Ident, thing string) { + if w.disableStutteringCheck { + return + } + pkg, name := w.fileAst.Name.Name, id.Name if !ast.IsExported(name) { // unexported name @@ -122,7 +184,7 @@ func (w *lintExported) checkStutter(id *ast.Ident, thing string) { Node: id, Confidence: 0.8, Category: "naming", - Failure: fmt.Sprintf("%s name will be used as %s.%s by other packages, and that stutters; consider calling this %s", thing, pkg, name, rem), + Failure: fmt.Sprintf("%s name will be used as %s.%s by other packages, and that %s; consider calling this %s", thing, pkg, name, w.stuttersMsg, rem), }) } } @@ -189,7 +251,7 @@ func (w *lintExported) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genD return } - if vs.Doc == nil && gd.Doc == nil { + if vs.Doc == nil && vs.Comment == nil && gd.Doc == nil { if genDeclMissingComments[gd] { return } @@ -207,15 +269,21 @@ func (w *lintExported) lintValueSpecDoc(vs *ast.ValueSpec, gd *ast.GenDecl, genD return } // If this GenDecl has parens and a comment, we don't check its comment form. - if gd.Lparen.IsValid() && gd.Doc != nil { + if gd.Doc != nil && gd.Lparen.IsValid() { return } // The relevant text to check will be on either vs.Doc or gd.Doc. // Use vs.Doc preferentially. - doc := vs.Doc - if doc == nil { + var doc *ast.CommentGroup + switch { + case vs.Doc != nil: + doc = vs.Doc + case vs.Comment != nil && gd.Doc == nil: + doc = vs.Comment + default: doc = gd.Doc } + prefix := name + " " s := normalizeText(doc.Text()) if !strings.HasPrefix(s, prefix) { diff --git a/vendor/github.com/mgechev/revive/rule/file-header.go b/vendor/github.com/mgechev/revive/rule/file-header.go index 6df974e91..76f548f51 100644 --- a/vendor/github.com/mgechev/revive/rule/file-header.go +++ b/vendor/github.com/mgechev/revive/rule/file-header.go @@ -1,29 +1,40 @@ package rule import ( + "fmt" "regexp" + "sync" "github.com/mgechev/revive/lint" ) // FileHeaderRule lints given else constructs. -type FileHeaderRule struct{} +type FileHeaderRule struct { + header string + sync.Mutex +} var ( - multiRegexp = regexp.MustCompile("^/\\*") + multiRegexp = regexp.MustCompile(`^/\*`) singleRegexp = regexp.MustCompile("^//") ) -// Apply applies the rule to given file. -func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - if len(arguments) != 1 { - panic(`invalid configuration for "file-header" rule`) +func (r *FileHeaderRule) configure(arguments lint.Arguments) { + r.Lock() + if r.header == "" { + checkNumberOfArguments(1, arguments, r.Name()) + var ok bool + r.header, ok = arguments[0].(string) + if !ok { + panic(fmt.Sprintf("invalid argument for \"file-header\" rule: first argument should be a string, got %T", arguments[0])) + } } + r.Unlock() +} - header, ok := arguments[0].(string) - if !ok { - panic(`invalid argument for "file-header" rule: first argument should be a string`) - } +// Apply applies the rule to given file. +func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) failure := []lint.Failure{ { @@ -44,26 +55,26 @@ func (r *FileHeaderRule) Apply(file *lint.File, arguments lint.Arguments) []lint comment := "" for _, c := range g.List { text := c.Text - if multiRegexp.Match([]byte(text)) { + if multiRegexp.MatchString(text) { text = text[2 : len(text)-2] - } else if singleRegexp.Match([]byte(text)) { + } else if singleRegexp.MatchString(text) { text = text[2:] } comment += text } - regex, err := regexp.Compile(header) + regex, err := regexp.Compile(r.header) if err != nil { panic(err.Error()) } - if !regex.Match([]byte(comment)) { + if !regex.MatchString(comment) { return failure } return nil } // Name returns the rule name. -func (r *FileHeaderRule) Name() string { +func (*FileHeaderRule) Name() string { return "file-header" } diff --git a/vendor/github.com/mgechev/revive/rule/flag-param.go b/vendor/github.com/mgechev/revive/rule/flag-param.go index 6cb6daea9..19a05f9fe 100644 --- a/vendor/github.com/mgechev/revive/rule/flag-param.go +++ b/vendor/github.com/mgechev/revive/rule/flag-param.go @@ -2,15 +2,16 @@ package rule import ( "fmt" - "github.com/mgechev/revive/lint" "go/ast" + + "github.com/mgechev/revive/lint" ) // FlagParamRule lints given else constructs. type FlagParamRule struct{} // Apply applies the rule to given file. -func (r *FlagParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*FlagParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -23,7 +24,7 @@ func (r *FlagParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure } // Name returns the rule name. -func (r *FlagParamRule) Name() string { +func (*FlagParamRule) Name() string { return "flag-parameter" } diff --git a/vendor/github.com/mgechev/revive/rule/function-length.go b/vendor/github.com/mgechev/revive/rule/function-length.go new file mode 100644 index 000000000..717ddbf7b --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/function-length.go @@ -0,0 +1,168 @@ +package rule + +import ( + "fmt" + "go/ast" + "reflect" + "sync" + + "github.com/mgechev/revive/lint" +) + +// FunctionLength lint. +type FunctionLength struct { + maxStmt int + maxLines int + sync.Mutex +} + +func (r *FunctionLength) configure(arguments lint.Arguments) { + r.Lock() + if r.maxLines == 0 { + maxStmt, maxLines := r.parseArguments(arguments) + r.maxStmt = int(maxStmt) + r.maxLines = int(maxLines) + } + r.Unlock() +} + +// Apply applies the rule to given file. +func (r *FunctionLength) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) + + var failures []lint.Failure + + walker := lintFuncLength{ + file: file, + maxStmt: r.maxStmt, + maxLines: r.maxLines, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*FunctionLength) Name() string { + return "function-length" +} + +func (*FunctionLength) parseArguments(arguments lint.Arguments) (maxStmt, maxLines int64) { + if len(arguments) != 2 { + panic(fmt.Sprintf(`invalid configuration for "function-length" rule, expected 2 arguments but got %d`, len(arguments))) + } + + maxStmt, maxStmtOk := arguments[0].(int64) + if !maxStmtOk { + panic(fmt.Sprintf(`invalid configuration value for max statements in "function-length" rule; need int64 but got %T`, arguments[0])) + } + if maxStmt < 0 { + panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxStmt)) + } + + maxLines, maxLinesOk := arguments[1].(int64) + if !maxLinesOk { + panic(fmt.Sprintf(`invalid configuration value for max lines in "function-length" rule; need int64 but got %T`, arguments[1])) + } + if maxLines < 0 { + panic(fmt.Sprintf(`the configuration value for max statements in "function-length" rule cannot be negative, got %d`, maxLines)) + } + + return maxStmt, maxLines +} + +type lintFuncLength struct { + file *lint.File + maxStmt int + maxLines int + onFailure func(lint.Failure) +} + +func (w lintFuncLength) Visit(n ast.Node) ast.Visitor { + node, ok := n.(*ast.FuncDecl) + if !ok { + return w + } + + body := node.Body + if body == nil || len(node.Body.List) == 0 { + return nil + } + + if w.maxStmt > 0 { + stmtCount := w.countStmts(node.Body.List) + if stmtCount > w.maxStmt { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("maximum number of statements per function exceeded; max %d but got %d", w.maxStmt, stmtCount), + Node: node, + }) + } + } + + if w.maxLines > 0 { + lineCount := w.countLines(node.Body) + if lineCount > w.maxLines { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("maximum number of lines per function exceeded; max %d but got %d", w.maxLines, lineCount), + Node: node, + }) + } + } + + return nil +} + +func (w lintFuncLength) countLines(b *ast.BlockStmt) int { + return w.file.ToPosition(b.End()).Line - w.file.ToPosition(b.Pos()).Line - 1 +} + +func (w lintFuncLength) countStmts(b []ast.Stmt) int { + count := 0 + for _, s := range b { + switch stmt := s.(type) { + case *ast.BlockStmt: + count += w.countStmts(stmt.List) + case *ast.IfStmt: + count += 1 + w.countBodyListStmts(stmt) + if stmt.Else != nil { + elseBody, ok := stmt.Else.(*ast.BlockStmt) + if ok { + count += w.countStmts(elseBody.List) + } + } + case *ast.ForStmt, *ast.RangeStmt, + *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt: + count += 1 + w.countBodyListStmts(stmt) + case *ast.CaseClause: + count += w.countStmts(stmt.Body) + case *ast.AssignStmt: + count += 1 + w.countFuncLitStmts(stmt.Rhs[0]) + case *ast.GoStmt: + count += 1 + w.countFuncLitStmts(stmt.Call.Fun) + case *ast.DeferStmt: + count += 1 + w.countFuncLitStmts(stmt.Call.Fun) + default: + count++ + } + } + + return count +} + +func (w lintFuncLength) countFuncLitStmts(stmt ast.Expr) int { + if block, ok := stmt.(*ast.FuncLit); ok { + return w.countStmts(block.Body.List) + } + return 0 +} + +func (w lintFuncLength) countBodyListStmts(t interface{}) int { + i := reflect.ValueOf(t).Elem().FieldByName(`Body`).Elem().FieldByName(`List`).Interface() + return w.countStmts(i.([]ast.Stmt)) +} diff --git a/vendor/github.com/mgechev/revive/rule/function-result-limit.go b/vendor/github.com/mgechev/revive/rule/function-result-limit.go index 1850fc419..5d2b87316 100644 --- a/vendor/github.com/mgechev/revive/rule/function-result-limit.go +++ b/vendor/github.com/mgechev/revive/rule/function-result-limit.go @@ -3,31 +3,42 @@ package rule import ( "fmt" "go/ast" + "sync" "github.com/mgechev/revive/lint" ) // FunctionResultsLimitRule lints given else constructs. -type FunctionResultsLimitRule struct{} +type FunctionResultsLimitRule struct { + max int + sync.Mutex +} -// Apply applies the rule to given file. -func (r *FunctionResultsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - if len(arguments) != 1 { - panic(`invalid configuration for "function-result-limit"`) - } +func (r *FunctionResultsLimitRule) configure(arguments lint.Arguments) { + r.Lock() + if r.max == 0 { + checkNumberOfArguments(1, arguments, r.Name()) - max, ok := arguments[0].(int64) // Alt. non panicking version - if !ok { - panic(fmt.Sprintf(`invalid value passed as return results number to the "function-result-limit" rule; need int64 but got %T`, arguments[0])) - } - if max < 0 { - panic(`the value passed as return results number to the "function-result-limit" rule cannot be negative`) + max, ok := arguments[0].(int64) // Alt. non panicking version + if !ok { + panic(fmt.Sprintf(`invalid value passed as return results number to the "function-result-limit" rule; need int64 but got %T`, arguments[0])) + } + if max < 0 { + panic(`the value passed as return results number to the "function-result-limit" rule cannot be negative`) + } + r.max = int(max) } + r.Unlock() +} + +// Apply applies the rule to given file. +func (r *FunctionResultsLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) var failures []lint.Failure walker := lintFunctionResultsNum{ - max: int(max), + max: r.max, onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, @@ -39,7 +50,7 @@ func (r *FunctionResultsLimitRule) Apply(file *lint.File, arguments lint.Argumen } // Name returns the rule name. -func (r *FunctionResultsLimitRule) Name() string { +func (*FunctionResultsLimitRule) Name() string { return "function-result-limit" } diff --git a/vendor/github.com/mgechev/revive/rule/get-return.go b/vendor/github.com/mgechev/revive/rule/get-return.go index 494ab6669..600a40fac 100644 --- a/vendor/github.com/mgechev/revive/rule/get-return.go +++ b/vendor/github.com/mgechev/revive/rule/get-return.go @@ -12,7 +12,7 @@ import ( type GetReturnRule struct{} // Apply applies the rule to given file. -func (r *GetReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*GetReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -25,7 +25,7 @@ func (r *GetReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure } // Name returns the rule name. -func (r *GetReturnRule) Name() string { +func (*GetReturnRule) Name() string { return "get-return" } diff --git a/vendor/github.com/mgechev/revive/rule/identical-branches.go b/vendor/github.com/mgechev/revive/rule/identical-branches.go index 094a79147..b1a69097f 100644 --- a/vendor/github.com/mgechev/revive/rule/identical-branches.go +++ b/vendor/github.com/mgechev/revive/rule/identical-branches.go @@ -10,7 +10,7 @@ import ( type IdenticalBranchesRule struct{} // Apply applies the rule to given file. -func (r *IdenticalBranchesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*IdenticalBranchesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -24,7 +24,7 @@ func (r *IdenticalBranchesRule) Apply(file *lint.File, _ lint.Arguments) []lint. } // Name returns the rule name. -func (r *IdenticalBranchesRule) Name() string { +func (*IdenticalBranchesRule) Name() string { return "identical-branches" } @@ -57,7 +57,7 @@ func (w *lintIdenticalBranches) Visit(node ast.Node) ast.Visitor { return w } -func (w *lintIdenticalBranches) identicalBranches(branches []*ast.BlockStmt) bool { +func (lintIdenticalBranches) identicalBranches(branches []*ast.BlockStmt) bool { if len(branches) < 2 { return false } diff --git a/vendor/github.com/mgechev/revive/rule/if-return.go b/vendor/github.com/mgechev/revive/rule/if-return.go index c275d2766..a6a3113ad 100644 --- a/vendor/github.com/mgechev/revive/rule/if-return.go +++ b/vendor/github.com/mgechev/revive/rule/if-return.go @@ -12,7 +12,7 @@ import ( type IfReturnRule struct{} // Apply applies the rule to given file. -func (r *IfReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*IfReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -26,7 +26,7 @@ func (r *IfReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { } // Name returns the rule name. -func (r *IfReturnRule) Name() string { +func (*IfReturnRule) Name() string { return "if-return" } diff --git a/vendor/github.com/mgechev/revive/rule/import-shadowing.go b/vendor/github.com/mgechev/revive/rule/import-shadowing.go index b78234c59..2bab704d0 100644 --- a/vendor/github.com/mgechev/revive/rule/import-shadowing.go +++ b/vendor/github.com/mgechev/revive/rule/import-shadowing.go @@ -13,7 +13,7 @@ import ( type ImportShadowingRule struct{} // Apply applies the rule to given file. -func (r *ImportShadowingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ImportShadowingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure importNames := map[string]struct{}{} @@ -23,7 +23,8 @@ func (r *ImportShadowingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa fileAst := file.AST walker := importShadowing{ - importNames: importNames, + packageNameIdent: fileAst.Name, + importNames: importNames, onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, @@ -36,7 +37,7 @@ func (r *ImportShadowingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa } // Name returns the rule name. -func (r *ImportShadowingRule) Name() string { +func (*ImportShadowingRule) Name() string { return "import-shadowing" } @@ -57,9 +58,10 @@ func getName(imp *ast.ImportSpec) string { } type importShadowing struct { - importNames map[string]struct{} - onFailure func(lint.Failure) - alreadySeen map[*ast.Object]struct{} + packageNameIdent *ast.Ident + importNames map[string]struct{} + onFailure func(lint.Failure) + alreadySeen map[*ast.Object]struct{} } // Visit visits AST nodes and checks if id nodes (ast.Ident) shadow an import name @@ -79,6 +81,10 @@ func (w importShadowing) Visit(n ast.Node) ast.Visitor { *ast.StructType: // skip analysis of struct type because struct fields can not shadow an import name return nil case *ast.Ident: + if n == w.packageNameIdent { + return nil // skip the ident corresponding to the package name of this file + } + id := n.Name if id == "_" { return w // skip _ id diff --git a/vendor/github.com/mgechev/revive/rule/imports-blacklist.go b/vendor/github.com/mgechev/revive/rule/imports-blacklist.go index 31ef901e5..710662815 100644 --- a/vendor/github.com/mgechev/revive/rule/imports-blacklist.go +++ b/vendor/github.com/mgechev/revive/rule/imports-blacklist.go @@ -2,38 +2,63 @@ package rule import ( "fmt" + "regexp" + "sync" "github.com/mgechev/revive/lint" ) // ImportsBlacklistRule lints given else constructs. -type ImportsBlacklistRule struct{} +type ImportsBlacklistRule struct { + blacklist []*regexp.Regexp + sync.Mutex +} + +var replaceRegexp = regexp.MustCompile(`/?\*\*/?`) + +func (r *ImportsBlacklistRule) configure(arguments lint.Arguments) { + r.Lock() + defer r.Unlock() + + if r.blacklist == nil { + r.blacklist = make([]*regexp.Regexp, 0) + + for _, arg := range arguments { + argStr, ok := arg.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the imports-blacklist rule. Expecting a string, got %T", arg)) + } + regStr, err := regexp.Compile(fmt.Sprintf(`(?m)"%s"$`, replaceRegexp.ReplaceAllString(argStr, `(\W|\w)*`))) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the imports-blacklist rule. Expecting %q to be a valid regular expression, got: %v", argStr, err)) + } + r.blacklist = append(r.blacklist, regStr) + } + } +} + +func (r *ImportsBlacklistRule) isBlacklisted(path string) bool { + for _, regex := range r.blacklist { + if regex.MatchString(path) { + return true + } + } + return false +} // Apply applies the rule to given file. func (r *ImportsBlacklistRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) + var failures []lint.Failure if file.IsTest() { return failures // skip, test file } - blacklist := make(map[string]bool, len(arguments)) - - for _, arg := range arguments { - argStr, ok := arg.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the imports-blacklist rule. Expecting a string, got %T", arg)) - } - // we add quotes if not present, because when parsed, the value of the AST node, will be quoted - if len(argStr) > 2 && argStr[0] != '"' && argStr[len(argStr)-1] != '"' { - argStr = fmt.Sprintf(`"%s"`, argStr) - } - blacklist[argStr] = true - } - for _, is := range file.AST.Imports { path := is.Path - if path != nil && blacklist[path.Value] { + if path != nil && r.isBlacklisted(path.Value) { failures = append(failures, lint.Failure{ Confidence: 1, Failure: "should not use the following blacklisted import: " + path.Value, @@ -47,6 +72,6 @@ func (r *ImportsBlacklistRule) Apply(file *lint.File, arguments lint.Arguments) } // Name returns the rule name. -func (r *ImportsBlacklistRule) Name() string { +func (*ImportsBlacklistRule) Name() string { return "imports-blacklist" } diff --git a/vendor/github.com/mgechev/revive/rule/increment-decrement.go b/vendor/github.com/mgechev/revive/rule/increment-decrement.go index 5d6b17671..34a8e1ec5 100644 --- a/vendor/github.com/mgechev/revive/rule/increment-decrement.go +++ b/vendor/github.com/mgechev/revive/rule/increment-decrement.go @@ -12,7 +12,7 @@ import ( type IncrementDecrementRule struct{} // Apply applies the rule to given file. -func (r *IncrementDecrementRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*IncrementDecrementRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -29,13 +29,12 @@ func (r *IncrementDecrementRule) Apply(file *lint.File, _ lint.Arguments) []lint } // Name returns the rule name. -func (r *IncrementDecrementRule) Name() string { +func (*IncrementDecrementRule) Name() string { return "increment-decrement" } type lintIncrementDecrement struct { file *lint.File - fileAst *ast.File onFailure func(lint.Failure) } diff --git a/vendor/github.com/mgechev/revive/rule/indent-error-flow.go b/vendor/github.com/mgechev/revive/rule/indent-error-flow.go index 4c9799b2a..e455801c4 100644 --- a/vendor/github.com/mgechev/revive/rule/indent-error-flow.go +++ b/vendor/github.com/mgechev/revive/rule/indent-error-flow.go @@ -11,7 +11,7 @@ import ( type IndentErrorFlowRule struct{} // Apply applies the rule to given file. -func (r *IndentErrorFlowRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*IndentErrorFlowRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -24,7 +24,7 @@ func (r *IndentErrorFlowRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa } // Name returns the rule name. -func (r *IndentErrorFlowRule) Name() string { +func (*IndentErrorFlowRule) Name() string { return "indent-error-flow" } diff --git a/vendor/github.com/mgechev/revive/rule/line-length-limit.go b/vendor/github.com/mgechev/revive/rule/line-length-limit.go index 5ee057079..9e512c1c2 100644 --- a/vendor/github.com/mgechev/revive/rule/line-length-limit.go +++ b/vendor/github.com/mgechev/revive/rule/line-length-limit.go @@ -6,28 +6,41 @@ import ( "fmt" "go/token" "strings" + "sync" "unicode/utf8" "github.com/mgechev/revive/lint" ) // LineLengthLimitRule lints given else constructs. -type LineLengthLimitRule struct{} +type LineLengthLimitRule struct { + max int + sync.Mutex +} -// Apply applies the rule to given file. -func (r *LineLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - if len(arguments) != 1 { - panic(`invalid configuration for "line-length-limit"`) - } +func (r *LineLengthLimitRule) configure(arguments lint.Arguments) { + r.Lock() + if r.max == 0 { + checkNumberOfArguments(1, arguments, r.Name()) + + max, ok := arguments[0].(int64) // Alt. non panicking version + if !ok || max < 0 { + panic(`invalid value passed as argument number to the "line-length-limit" rule`) + } - max, ok := arguments[0].(int64) // Alt. non panicking version - if !ok || max < 0 { - panic(`invalid value passed as argument number to the "line-length-limit" rule`) + r.max = int(max) } + r.Unlock() +} + +// Apply applies the rule to given file. +func (r *LineLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) var failures []lint.Failure + checker := lintLineLengthNum{ - max: int(max), + max: r.max, file: file, onFailure: func(failure lint.Failure) { failures = append(failures, failure) @@ -40,7 +53,7 @@ func (r *LineLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) [ } // Name returns the rule name. -func (r *LineLengthLimitRule) Name() string { +func (*LineLengthLimitRule) Name() string { return "line-length-limit" } @@ -57,7 +70,7 @@ func (r lintLineLengthNum) check() { s := bufio.NewScanner(f) for s.Scan() { t := s.Text() - t = strings.Replace(t, "\t", spaces, -1) + t = strings.ReplaceAll(t, "\t", spaces) c := utf8.RuneCountInString(t) if c > r.max { r.onFailure(lint.Failure{ diff --git a/vendor/github.com/mgechev/revive/rule/max-public-structs.go b/vendor/github.com/mgechev/revive/rule/max-public-structs.go index 9a2d07cbc..e39f49c69 100644 --- a/vendor/github.com/mgechev/revive/rule/max-public-structs.go +++ b/vendor/github.com/mgechev/revive/rule/max-public-structs.go @@ -2,20 +2,40 @@ package rule import ( "go/ast" - "strings" + "sync" "github.com/mgechev/revive/lint" ) // MaxPublicStructsRule lints given else constructs. -type MaxPublicStructsRule struct{} +type MaxPublicStructsRule struct { + max int64 + sync.Mutex +} + +func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) { + r.Lock() + if r.max < 1 { + checkNumberOfArguments(1, arguments, r.Name()) + + max, ok := arguments[0].(int64) // Alt. non panicking version + if !ok { + panic(`invalid value passed as argument number to the "max-public-structs" rule`) + } + r.max = max + } + r.Unlock() +} // Apply applies the rule to given file. func (r *MaxPublicStructsRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) + var failures []lint.Failure fileAst := file.AST + walker := &lintMaxPublicStructs{ fileAst: fileAst, onFailure: func(failure lint.Failure) { @@ -25,12 +45,7 @@ func (r *MaxPublicStructsRule) Apply(file *lint.File, arguments lint.Arguments) ast.Walk(walker, fileAst) - max, ok := arguments[0].(int64) // Alt. non panicking version - if !ok { - panic(`invalid value passed as argument number to the "max-public-structs" rule`) - } - - if walker.current > max { + if walker.current > r.max { walker.onFailure(lint.Failure{ Failure: "you have exceeded the maximum number of public struct declarations", Confidence: 1, @@ -43,7 +58,7 @@ func (r *MaxPublicStructsRule) Apply(file *lint.File, arguments lint.Arguments) } // Name returns the rule name. -func (r *MaxPublicStructsRule) Name() string { +func (*MaxPublicStructsRule) Name() string { return "max-public-structs" } @@ -61,7 +76,6 @@ func (w *lintMaxPublicStructs) Visit(n ast.Node) ast.Visitor { if strings.ToUpper(first) == first { w.current++ } - break } return w } diff --git a/vendor/github.com/mgechev/revive/rule/modifies-param.go b/vendor/github.com/mgechev/revive/rule/modifies-param.go index 55136e6c8..a68ae2501 100644 --- a/vendor/github.com/mgechev/revive/rule/modifies-param.go +++ b/vendor/github.com/mgechev/revive/rule/modifies-param.go @@ -11,7 +11,7 @@ import ( type ModifiesParamRule struct{} // Apply applies the rule to given file. -func (r *ModifiesParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ModifiesParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -24,7 +24,7 @@ func (r *ModifiesParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fail } // Name returns the rule name. -func (r *ModifiesParamRule) Name() string { +func (*ModifiesParamRule) Name() string { return "modifies-parameter" } diff --git a/vendor/github.com/mgechev/revive/rule/modifies-value-receiver.go b/vendor/github.com/mgechev/revive/rule/modifies-value-receiver.go index 4fe22ddf3..34e651557 100644 --- a/vendor/github.com/mgechev/revive/rule/modifies-value-receiver.go +++ b/vendor/github.com/mgechev/revive/rule/modifies-value-receiver.go @@ -11,7 +11,7 @@ import ( type ModifiesValRecRule struct{} // Apply applies the rule to given file. -func (r *ModifiesValRecRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ModifiesValRecRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -26,7 +26,7 @@ func (r *ModifiesValRecRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fai } // Name returns the rule name. -func (r *ModifiesValRecRule) Name() string { +func (*ModifiesValRecRule) Name() string { return "modifies-value-receiver" } diff --git a/vendor/github.com/mgechev/revive/rule/nested-structs.go b/vendor/github.com/mgechev/revive/rule/nested-structs.go new file mode 100644 index 000000000..b4f7352db --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/nested-structs.go @@ -0,0 +1,67 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// NestedStructs lints nested structs. +type NestedStructs struct{} + +// Apply applies the rule to given file. +func (*NestedStructs) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + walker := &lintNestedStructs{ + fileAST: file.AST, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + + ast.Walk(walker, file.AST) + + return failures +} + +// Name returns the rule name. +func (*NestedStructs) Name() string { + return "nested-structs" +} + +type lintNestedStructs struct { + fileAST *ast.File + onFailure func(lint.Failure) +} + +func (l *lintNestedStructs) Visit(n ast.Node) ast.Visitor { + switch v := n.(type) { + case *ast.FuncDecl: + if v.Body != nil { + ast.Walk(l, v.Body) + } + return nil + case *ast.Field: + filter := func(n ast.Node) bool { + switch n.(type) { + case *ast.StructType: + return true + default: + return false + } + } + structs := pick(v, filter, nil) + for _, s := range structs { + l.onFailure(lint.Failure{ + Failure: "no nested structs are allowed", + Category: "style", + Node: s, + Confidence: 1, + }) + } + return nil // no need to visit (again) the field + } + + return l +} diff --git a/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go b/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go new file mode 100644 index 000000000..88928bb98 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go @@ -0,0 +1,77 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// OptimizeOperandsOrderRule lints given else constructs. +type OptimizeOperandsOrderRule struct{} + +// Apply applies the rule to given file. +func (*OptimizeOperandsOrderRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + w := lintOptimizeOperandsOrderlExpr{ + onFailure: onFailure, + } + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*OptimizeOperandsOrderRule) Name() string { + return "optimize-operands-order" +} + +type lintOptimizeOperandsOrderlExpr struct { + onFailure func(failure lint.Failure) +} + +// Visit checks boolean AND and OR expressions to determine +// if swapping their operands may result in an execution speedup. +func (w lintOptimizeOperandsOrderlExpr) Visit(node ast.Node) ast.Visitor { + binExpr, ok := node.(*ast.BinaryExpr) + if !ok { + return w + } + + switch binExpr.Op { + case token.LAND, token.LOR: + default: + return w + } + + isCaller := func(n ast.Node) bool { + _, ok := n.(*ast.CallExpr) + return ok + } + + // check if the left sub-expression contains a function call + nodes := pick(binExpr.X, isCaller, nil) + if len(nodes) < 1 { + return w + } + + // check if the right sub-expression does not contain a function call + nodes = pick(binExpr.Y, isCaller, nil) + if len(nodes) > 0 { + return w + } + + newExpr := ast.BinaryExpr{X: binExpr.Y, Y: binExpr.X, Op: binExpr.Op} + w.onFailure(lint.Failure{ + Failure: fmt.Sprintf("for better performance '%v' might be rewritten as '%v'", gofmt(binExpr), gofmt(&newExpr)), + Node: node, + Category: "optimization", + Confidence: 0.3, + }) + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/package-comments.go b/vendor/github.com/mgechev/revive/rule/package-comments.go index 00fc5bb91..33963ab97 100644 --- a/vendor/github.com/mgechev/revive/rule/package-comments.go +++ b/vendor/github.com/mgechev/revive/rule/package-comments.go @@ -5,6 +5,7 @@ import ( "go/ast" "go/token" "strings" + "sync" "github.com/mgechev/revive/lint" ) @@ -14,13 +15,15 @@ import ( // This has a notable false positive in that a package comment // could rightfully appear in a different file of the same package, // but that's not easy to fix since this linter is file-oriented. -type PackageCommentsRule struct{} +type PackageCommentsRule struct { + checkPackageCommentCache sync.Map +} // Apply applies the rule to given file. func (r *PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure - if isTest(file) { + if file.IsTest() { return failures } @@ -29,13 +32,13 @@ func (r *PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa } fileAst := file.AST - w := &lintPackageComments{fileAst, file, onFailure} + w := &lintPackageComments{fileAst, file, onFailure, r} ast.Walk(w, fileAst) return failures } // Name returns the rule name. -func (r *PackageCommentsRule) Name() string { +func (*PackageCommentsRule) Name() string { return "package-comments" } @@ -43,6 +46,49 @@ type lintPackageComments struct { fileAst *ast.File file *lint.File onFailure func(lint.Failure) + rule *PackageCommentsRule +} + +func (l *lintPackageComments) checkPackageComment() []lint.Failure { + // deduplicate warnings in package + if _, exists := l.rule.checkPackageCommentCache.LoadOrStore(l.file.Pkg, struct{}{}); exists { + return nil + } + var docFile *ast.File // which name is doc.go + var packageFile *ast.File // which name is $package.go + var firstFile *ast.File + var firstFileName string + for name, file := range l.file.Pkg.Files() { + if file.AST.Doc != nil { + return nil + } + if name == "doc.go" { + docFile = file.AST + } + if name == file.AST.Name.String()+".go" { + packageFile = file.AST + } + if firstFileName == "" || firstFileName > name { + firstFile = file.AST + firstFileName = name + } + } + // prefer warning on doc.go, $package.go over first file + if docFile == nil { + docFile = packageFile + } + if docFile == nil { + docFile = firstFile + } + if docFile != nil { + return []lint.Failure{{ + Category: "comments", + Node: docFile, + Confidence: 1, + Failure: "should have a package comment", + }} + } + return nil } func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor { @@ -50,7 +96,6 @@ func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor { return nil } - const ref = styleGuideBase + "#package-comments" prefix := "Package " + l.fileAst.Name.Name + " " // Look for a detached package comment. @@ -90,12 +135,9 @@ func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor { } if l.fileAst.Doc == nil { - l.onFailure(lint.Failure{ - Category: "comments", - Node: l.fileAst, - Confidence: 0.2, - Failure: "should have a package comment, unless it's in another file for this package", - }) + for _, failure := range l.checkPackageComment() { + l.onFailure(failure) + } return nil } s := l.fileAst.Doc.Text() diff --git a/vendor/github.com/mgechev/revive/rule/range-val-address.go b/vendor/github.com/mgechev/revive/rule/range-val-address.go index 18554825a..51ad8e108 100644 --- a/vendor/github.com/mgechev/revive/rule/range-val-address.go +++ b/vendor/github.com/mgechev/revive/rule/range-val-address.go @@ -4,6 +4,7 @@ import ( "fmt" "go/ast" "go/token" + "strings" "github.com/mgechev/revive/lint" ) @@ -12,26 +13,29 @@ import ( type RangeValAddress struct{} // Apply applies the rule to given file. -func (r *RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure walker := rangeValAddress{ + file: file, onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, } + file.Pkg.TypeCheck() ast.Walk(walker, file.AST) return failures } // Name returns the rule name. -func (r *RangeValAddress) Name() string { +func (*RangeValAddress) Name() string { return "range-val-address" } type rangeValAddress struct { + file *lint.File onFailure func(lint.Failure) } @@ -46,17 +50,24 @@ func (w rangeValAddress) Visit(node ast.Node) ast.Visitor { return w } + valueIsStarExpr := false + if t := w.file.Pkg.TypeOf(value); t != nil { + valueIsStarExpr = strings.HasPrefix(t.String(), "*") + } + ast.Walk(rangeBodyVisitor{ - valueID: value.Obj, - onFailure: w.onFailure, + valueIsStarExpr: valueIsStarExpr, + valueID: value.Obj, + onFailure: w.onFailure, }, n.Body) return w } type rangeBodyVisitor struct { - valueID *ast.Object - onFailure func(lint.Failure) + valueIsStarExpr bool + valueID *ast.Object + onFailure func(lint.Failure) } func (bw rangeBodyVisitor) Visit(node ast.Node) ast.Visitor { @@ -77,31 +88,69 @@ func (bw rangeBodyVisitor) Visit(node ast.Node) ast.Visitor { for _, exp := range asgmt.Rhs { switch e := exp.(type) { - case *ast.UnaryExpr: // e.g. ...&value + case *ast.UnaryExpr: // e.g. ...&value, ...&value.id if bw.isAccessingRangeValueAddress(e) { bw.onFailure(bw.newFailure(e)) } case *ast.CallExpr: if fun, ok := e.Fun.(*ast.Ident); ok && fun.Name == "append" { // e.g. ...append(arr, &value) for _, v := range e.Args { - if bw.isAccessingRangeValueAddress(v) { - bw.onFailure(bw.newFailure(e)) + if lit, ok := v.(*ast.CompositeLit); ok { // e.g. ...append(arr, v{id:&value}) + bw.checkCompositeLit(lit) + continue + } + if bw.isAccessingRangeValueAddress(v) { // e.g. ...append(arr, &value) + bw.onFailure(bw.newFailure(v)) } } } + case *ast.CompositeLit: // e.g. ...v{id:&value} + bw.checkCompositeLit(e) } } return bw } +func (bw rangeBodyVisitor) checkCompositeLit(comp *ast.CompositeLit) { + for _, exp := range comp.Elts { + e, ok := exp.(*ast.KeyValueExpr) + if !ok { + continue + } + if bw.isAccessingRangeValueAddress(e.Value) { + bw.onFailure(bw.newFailure(e.Value)) + } + } +} + func (bw rangeBodyVisitor) isAccessingRangeValueAddress(exp ast.Expr) bool { u, ok := exp.(*ast.UnaryExpr) if !ok { return false } + if u.Op != token.AND { + return false + } + v, ok := u.X.(*ast.Ident) - return ok && u.Op == token.AND && v.Obj == bw.valueID + if !ok { + var s *ast.SelectorExpr + s, ok = u.X.(*ast.SelectorExpr) + if !ok { + return false + } + v, ok = s.X.(*ast.Ident) + if !ok { + return false + } + + if bw.valueIsStarExpr { // check type of value + return false + } + } + + return ok && v.Obj == bw.valueID } func (bw rangeBodyVisitor) newFailure(node ast.Node) lint.Failure { diff --git a/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go b/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go index 857787be3..1e85d0d0d 100644 --- a/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go +++ b/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go @@ -11,7 +11,7 @@ import ( type RangeValInClosureRule struct{} // Apply applies the rule to given file. -func (r *RangeValInClosureRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*RangeValInClosureRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure walker := rangeValInClosure{ @@ -26,7 +26,7 @@ func (r *RangeValInClosureRule) Apply(file *lint.File, _ lint.Arguments) []lint. } // Name returns the rule name. -func (r *RangeValInClosureRule) Name() string { +func (*RangeValInClosureRule) Name() string { return "range-val-in-closure" } @@ -35,7 +35,6 @@ type rangeValInClosure struct { } func (w rangeValInClosure) Visit(node ast.Node) ast.Visitor { - // Find the variables updated by the loop statement. var vars []*ast.Ident addVar := func(expr ast.Expr) { @@ -87,15 +86,25 @@ func (w rangeValInClosure) Visit(node ast.Node) ast.Visitor { if !ok { return w } + if lit.Type == nil { // Not referring to a variable (e.g. struct field name) return w } - ast.Inspect(lit.Body, func(n ast.Node) bool { + + var inspector func(n ast.Node) bool + inspector = func(n ast.Node) bool { + kv, ok := n.(*ast.KeyValueExpr) + if ok { + // do not check identifiers acting as key in key-value expressions (see issue #637) + ast.Inspect(kv.Value, inspector) + return false + } id, ok := n.(*ast.Ident) if !ok || id.Obj == nil { return true } + for _, v := range vars { if v.Obj == id.Obj { w.onFailure(lint.Failure{ @@ -106,6 +115,7 @@ func (w rangeValInClosure) Visit(node ast.Node) ast.Visitor { } } return true - }) + } + ast.Inspect(lit.Body, inspector) return w } diff --git a/vendor/github.com/mgechev/revive/rule/range.go b/vendor/github.com/mgechev/revive/rule/range.go index d18492c71..9d483a673 100644 --- a/vendor/github.com/mgechev/revive/rule/range.go +++ b/vendor/github.com/mgechev/revive/rule/range.go @@ -12,7 +12,7 @@ import ( type RangeRule struct{} // Apply applies the rule to given file. -func (r *RangeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*RangeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -25,7 +25,7 @@ func (r *RangeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { } // Name returns the rule name. -func (r *RangeRule) Name() string { +func (*RangeRule) Name() string { return "range" } diff --git a/vendor/github.com/mgechev/revive/rule/receiver-naming.go b/vendor/github.com/mgechev/revive/rule/receiver-naming.go index 589d5f0ef..d79bb9fe8 100644 --- a/vendor/github.com/mgechev/revive/rule/receiver-naming.go +++ b/vendor/github.com/mgechev/revive/rule/receiver-naming.go @@ -4,6 +4,7 @@ import ( "fmt" "go/ast" + "github.com/mgechev/revive/internal/typeparams" "github.com/mgechev/revive/lint" ) @@ -11,7 +12,7 @@ import ( type ReceiverNamingRule struct{} // Apply applies the rule to given file. -func (r *ReceiverNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*ReceiverNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -28,7 +29,7 @@ func (r *ReceiverNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fai } // Name returns the rule name. -func (r *ReceiverNamingRule) Name() string { +func (*ReceiverNamingRule) Name() string { return "receiver-naming" } @@ -47,7 +48,6 @@ func (w lintReceiverName) Visit(n ast.Node) ast.Visitor { return w } name := names[0].Name - const ref = styleGuideBase + "#receiver-names" if name == "_" { w.onFailure(lint.Failure{ Node: n, @@ -66,7 +66,7 @@ func (w lintReceiverName) Visit(n ast.Node) ast.Visitor { }) return w } - recv := receiverType(fn) + recv := typeparams.ReceiverType(fn) if prev, ok := w.typeReceiver[recv]; ok && prev != name { w.onFailure(lint.Failure{ Node: n, diff --git a/vendor/github.com/mgechev/revive/rule/redefines-builtin-id.go b/vendor/github.com/mgechev/revive/rule/redefines-builtin-id.go index 947b8aac7..23dd85a7a 100644 --- a/vendor/github.com/mgechev/revive/rule/redefines-builtin-id.go +++ b/vendor/github.com/mgechev/revive/rule/redefines-builtin-id.go @@ -2,116 +2,125 @@ package rule import ( "fmt" - "github.com/mgechev/revive/lint" "go/ast" "go/token" + + "github.com/mgechev/revive/lint" ) +var builtInConstAndVars = map[string]bool{ + "true": true, + "false": true, + "iota": true, + "nil": true, +} + +var builtFunctions = map[string]bool{ + "append": true, + "cap": true, + "close": true, + "complex": true, + "copy": true, + "delete": true, + "imag": true, + "len": true, + "make": true, + "new": true, + "panic": true, + "print": true, + "println": true, + "real": true, + "recover": true, +} + +var builtInTypes = map[string]bool{ + "bool": true, + "byte": true, + "complex128": true, + "complex64": true, + "error": true, + "float32": true, + "float64": true, + "int": true, + "int16": true, + "int32": true, + "int64": true, + "int8": true, + "rune": true, + "string": true, + "uint": true, + "uint16": true, + "uint32": true, + "uint64": true, + "uint8": true, + "uintptr": true, + "any": true, +} + // RedefinesBuiltinIDRule warns when a builtin identifier is shadowed. type RedefinesBuiltinIDRule struct{} // Apply applies the rule to given file. -func (r *RedefinesBuiltinIDRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*RedefinesBuiltinIDRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure - var builtInConstAndVars = map[string]bool{ - "true": true, - "false": true, - "iota": true, - "nil": true, - } - - var builtFunctions = map[string]bool{ - "append": true, - "cap": true, - "close": true, - "complex": true, - "copy": true, - "delete": true, - "imag": true, - "len": true, - "make": true, - "new": true, - "panic": true, - "print": true, - "println": true, - "real": true, - "recover": true, - } - - var builtInTypes = map[string]bool{ - "ComplexType": true, - "FloatType": true, - "IntegerType": true, - "Type": true, - "Type1": true, - "bool": true, - "byte": true, - "complex128": true, - "complex64": true, - "error": true, - "float32": true, - "float64": true, - "int": true, - "int16": true, - "int32": true, - "int64": true, - "int8": true, - "rune": true, - "string": true, - "uint": true, - "uint16": true, - "uint32": true, - "uint64": true, - "uint8": true, - "uintptr": true, - } - onFailure := func(failure lint.Failure) { failures = append(failures, failure) } astFile := file.AST - w := &lintRedefinesBuiltinID{builtInConstAndVars, builtFunctions, builtInTypes, onFailure} + w := &lintRedefinesBuiltinID{onFailure} ast.Walk(w, astFile) return failures } // Name returns the rule name. -func (r *RedefinesBuiltinIDRule) Name() string { +func (*RedefinesBuiltinIDRule) Name() string { return "redefines-builtin-id" } type lintRedefinesBuiltinID struct { - constsAndVars map[string]bool - funcs map[string]bool - types map[string]bool - onFailure func(lint.Failure) + onFailure func(lint.Failure) } func (w *lintRedefinesBuiltinID) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.GenDecl: - if n.Tok != token.TYPE { - return nil // skip if not type declaration - } - typeSpec, ok := n.Specs[0].(*ast.TypeSpec) - if !ok { - return nil - } - id := typeSpec.Name.Name - if w.types[id] { - w.addFailure(n, fmt.Sprintf("redefinition of the built-in type %s", id)) + switch n.Tok { + case token.TYPE: + typeSpec, ok := n.Specs[0].(*ast.TypeSpec) + if !ok { + return nil + } + id := typeSpec.Name.Name + if ok, bt := w.isBuiltIn(id); ok { + w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, id)) + } + case token.VAR, token.CONST: + for _, vs := range n.Specs { + valSpec, ok := vs.(*ast.ValueSpec) + if !ok { + continue + } + for _, name := range valSpec.Names { + if ok, bt := w.isBuiltIn(name.Name); ok { + w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, name)) + } + } + } + default: + return nil // skip if not type/var/const declaration } + case *ast.FuncDecl: if n.Recv != nil { return w // skip methods } id := n.Name.Name - if w.funcs[id] { - w.addFailure(n, fmt.Sprintf("redefinition of the built-in function %s", id)) + if ok, bt := w.isBuiltIn(id); ok { + w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, id)) } case *ast.AssignStmt: for _, e := range n.Lhs { @@ -120,13 +129,19 @@ func (w *lintRedefinesBuiltinID) Visit(node ast.Node) ast.Visitor { continue } - if w.constsAndVars[id.Name] { + if ok, bt := w.isBuiltIn(id.Name); ok { var msg string - if n.Tok == token.DEFINE { - msg = fmt.Sprintf("assignment creates a shadow of built-in identifier %s", id.Name) - } else { - msg = fmt.Sprintf("assignment modifies built-in identifier %s", id.Name) + switch bt { + case "constant or variable": + if n.Tok == token.DEFINE { + msg = fmt.Sprintf("assignment creates a shadow of built-in identifier %s", id.Name) + } else { + msg = fmt.Sprintf("assignment modifies built-in identifier %s", id.Name) + } + default: + msg = fmt.Sprintf("redefinition of the built-in %s %s", bt, id) } + w.addFailure(n, msg) } } @@ -143,3 +158,19 @@ func (w lintRedefinesBuiltinID) addFailure(node ast.Node, msg string) { Failure: msg, }) } + +func (lintRedefinesBuiltinID) isBuiltIn(id string) (r bool, builtInKind string) { + if builtFunctions[id] { + return true, "function" + } + + if builtInConstAndVars[id] { + return true, "constant or variable" + } + + if builtInTypes[id] { + return true, "type" + } + + return false, "" +} diff --git a/vendor/github.com/mgechev/revive/rule/string-format.go b/vendor/github.com/mgechev/revive/rule/string-format.go new file mode 100644 index 000000000..e7841e8c3 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/string-format.go @@ -0,0 +1,281 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + "regexp" + "strconv" + + "github.com/mgechev/revive/lint" +) + +// #region Revive API + +// StringFormatRule lints strings and/or comments according to a set of regular expressions given as Arguments +type StringFormatRule struct{} + +// Apply applies the rule to the given file. +func (*StringFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintStringFormatRule{onFailure: onFailure} + w.parseArguments(arguments) + ast.Walk(w, file.AST) + + return failures +} + +// Name returns the rule name. +func (*StringFormatRule) Name() string { + return "string-format" +} + +// ParseArgumentsTest is a public wrapper around w.parseArguments used for testing. Returns the error message provided to panic, or nil if no error was encountered +func (StringFormatRule) ParseArgumentsTest(arguments lint.Arguments) *string { + w := lintStringFormatRule{} + c := make(chan interface{}) + // Parse the arguments in a goroutine, defer a recover() call, return the error encountered (or nil if there was no error) + go func() { + defer func() { + err := recover() + c <- err + }() + w.parseArguments(arguments) + }() + err := <-c + if err != nil { + e := fmt.Sprintf("%s", err) + return &e + } + return nil +} + +// #endregion + +// #region Internal structure + +type lintStringFormatRule struct { + onFailure func(lint.Failure) + rules []stringFormatSubrule +} + +type stringFormatSubrule struct { + parent *lintStringFormatRule + scope stringFormatSubruleScope + regexp *regexp.Regexp + errorMessage string +} + +type stringFormatSubruleScope struct { + funcName string // Function name the rule is scoped to + argument int // (optional) Which argument in calls to the function is checked against the rule (the first argument is checked by default) + field string // (optional) If the argument to be checked is a struct, which member of the struct is checked against the rule (top level members only) +} + +// Regex inserted to match valid function/struct field identifiers +const identRegex = "[_A-Za-z][_A-Za-z0-9]*" + +var parseStringFormatScope = regexp.MustCompile( + fmt.Sprintf("^(%s(?:\\.%s)?)(?:\\[([0-9]+)\\](?:\\.(%s))?)?$", identRegex, identRegex, identRegex)) + +// #endregion + +// #region Argument parsing + +func (w *lintStringFormatRule) parseArguments(arguments lint.Arguments) { + for i, argument := range arguments { + scope, regex, errorMessage := w.parseArgument(argument, i) + w.rules = append(w.rules, stringFormatSubrule{ + parent: w, + scope: scope, + regexp: regex, + errorMessage: errorMessage, + }) + } +} + +func (w lintStringFormatRule) parseArgument(argument interface{}, ruleNum int) (scope stringFormatSubruleScope, regex *regexp.Regexp, errorMessage string) { + g, ok := argument.([]interface{}) // Cast to generic slice first + if !ok { + w.configError("argument is not a slice", ruleNum, 0) + } + if len(g) < 2 { + w.configError("less than two slices found in argument, scope and regex are required", ruleNum, len(g)-1) + } + rule := make([]string, len(g)) + for i, obj := range g { + val, ok := obj.(string) + if !ok { + w.configError("unexpected value, string was expected", ruleNum, i) + } + rule[i] = val + } + + // Validate scope and regex length + if rule[0] == "" { + w.configError("empty scope provided", ruleNum, 0) + } else if len(rule[1]) < 2 { + w.configError("regex is too small (regexes should begin and end with '/')", ruleNum, 1) + } + + // Parse rule scope + scope = stringFormatSubruleScope{} + matches := parseStringFormatScope.FindStringSubmatch(rule[0]) + if matches == nil { + // The rule's scope didn't match the parsing regex at all, probably a configuration error + w.parseError("unable to parse rule scope", ruleNum, 0) + } else if len(matches) != 4 { + // The rule's scope matched the parsing regex, but an unexpected number of submatches was returned, probably a bug + w.parseError(fmt.Sprintf("unexpected number of submatches when parsing scope: %d, expected 4", len(matches)), ruleNum, 0) + } + scope.funcName = matches[1] + if len(matches[2]) > 0 { + var err error + scope.argument, err = strconv.Atoi(matches[2]) + if err != nil { + w.parseError("unable to parse argument number in rule scope", ruleNum, 0) + } + } + if len(matches[3]) > 0 { + scope.field = matches[3] + } + + // Strip / characters from the beginning and end of rule[1] before compiling + regex, err := regexp.Compile(rule[1][1 : len(rule[1])-1]) + if err != nil { + w.parseError(fmt.Sprintf("unable to compile %s as regexp", rule[1]), ruleNum, 1) + } + + // Use custom error message if provided + if len(rule) == 3 { + errorMessage = rule[2] + } + return scope, regex, errorMessage +} + +// Report an invalid config, this is specifically the user's fault +func (lintStringFormatRule) configError(msg string, ruleNum, option int) { + panic(fmt.Sprintf("invalid configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)) +} + +// Report a general config parsing failure, this may be the user's fault, but it isn't known for certain +func (lintStringFormatRule) parseError(msg string, ruleNum, option int) { + panic(fmt.Sprintf("failed to parse configuration for string-format: %s [argument %d, option %d]", msg, ruleNum, option)) +} + +// #endregion + +// #region Node traversal + +func (w lintStringFormatRule) Visit(node ast.Node) ast.Visitor { + // First, check if node is a call expression + call, ok := node.(*ast.CallExpr) + if !ok { + return w + } + + // Get the name of the call expression to check against rule scope + callName, ok := w.getCallName(call) + if !ok { + return w + } + + for _, rule := range w.rules { + if rule.scope.funcName == callName { + rule.Apply(call) + } + } + + return w +} + +// Return the name of a call expression in the form of package.Func or Func +func (lintStringFormatRule) getCallName(call *ast.CallExpr) (callName string, ok bool) { + if ident, ok := call.Fun.(*ast.Ident); ok { + // Local function call + return ident.Name, true + } + + if selector, ok := call.Fun.(*ast.SelectorExpr); ok { + // Scoped function call + scope, ok := selector.X.(*ast.Ident) + if !ok { + return "", false + } + return scope.Name + "." + selector.Sel.Name, true + } + + return "", false +} + +// #endregion + +// #region Linting logic + +// Apply a single format rule to a call expression (should be done after verifying the that the call expression matches the rule's scope) +func (r *stringFormatSubrule) Apply(call *ast.CallExpr) { + if len(call.Args) <= r.scope.argument { + return + } + + arg := call.Args[r.scope.argument] + var lit *ast.BasicLit + if len(r.scope.field) > 0 { + // Try finding the scope's Field, treating arg as a composite literal + composite, ok := arg.(*ast.CompositeLit) + if !ok { + return + } + for _, el := range composite.Elts { + kv, ok := el.(*ast.KeyValueExpr) + if !ok { + continue + } + key, ok := kv.Key.(*ast.Ident) + if !ok || key.Name != r.scope.field { + continue + } + + // We're now dealing with the exact field in the rule's scope, so if anything fails, we can safely return instead of continuing the loop + lit, ok = kv.Value.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return + } + } + } else { + var ok bool + // Treat arg as a string literal + lit, ok = arg.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return + } + } + // Unquote the string literal before linting + unquoted := lit.Value[1 : len(lit.Value)-1] + r.lintMessage(unquoted, lit) +} + +func (r *stringFormatSubrule) lintMessage(s string, node ast.Node) { + // Fail if the string doesn't match the user's regex + if r.regexp.MatchString(s) { + return + } + var failure string + if len(r.errorMessage) > 0 { + failure = r.errorMessage + } else { + failure = fmt.Sprintf("string literal doesn't match user defined regex /%s/", r.regexp.String()) + } + r.parent.onFailure(lint.Failure{ + Confidence: 1, + Failure: failure, + Node: node, + }) +} + +// #endregion diff --git a/vendor/github.com/mgechev/revive/rule/string-of-int.go b/vendor/github.com/mgechev/revive/rule/string-of-int.go index 38f453a4a..3bec1d6ac 100644 --- a/vendor/github.com/mgechev/revive/rule/string-of-int.go +++ b/vendor/github.com/mgechev/revive/rule/string-of-int.go @@ -11,7 +11,7 @@ import ( type StringOfIntRule struct{} // Apply applies the rule to given file. -func (r *StringOfIntRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*StringOfIntRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -28,7 +28,7 @@ func (r *StringOfIntRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failur } // Name returns the rule name. -func (r *StringOfIntRule) Name() string { +func (*StringOfIntRule) Name() string { return "string-of-int" } @@ -54,7 +54,7 @@ func (w *lintStringInt) Visit(node ast.Node) ast.Visitor { w.onFailure(lint.Failure{ Confidence: 1, Node: ce, - Failure: "dubious convertion of an integer into a string, use strconv.Itoa", + Failure: "dubious conversion of an integer into a string, use strconv.Itoa", }) return w diff --git a/vendor/github.com/mgechev/revive/rule/struct-tag.go b/vendor/github.com/mgechev/revive/rule/struct-tag.go index 57cf8103a..3accf58fb 100644 --- a/vendor/github.com/mgechev/revive/rule/struct-tag.go +++ b/vendor/github.com/mgechev/revive/rule/struct-tag.go @@ -14,7 +14,7 @@ import ( type StructTagRule struct{} // Apply applies the rule to given file. -func (r *StructTagRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*StructTagRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -29,13 +29,14 @@ func (r *StructTagRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure } // Name returns the rule name. -func (r *StructTagRule) Name() string { +func (*StructTagRule) Name() string { return "struct-tag" } type lintStructTagRule struct { - onFailure func(lint.Failure) - usedTagNbr map[string]bool // list of used tag numbers + onFailure func(lint.Failure) + usedTagNbr map[int]bool // list of used tag numbers + usedTagName map[string]bool // list of used tag keys } func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { @@ -44,7 +45,8 @@ func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { if n.Fields == nil || n.Fields.NumFields() < 1 { return nil // skip empty structs } - w.usedTagNbr = map[string]bool{} // init + w.usedTagNbr = map[int]bool{} // init + w.usedTagName = map[string]bool{} // init for _, f := range n.Fields.List { if f.Tag != nil { w.checkTaggedField(f) @@ -53,7 +55,53 @@ func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor { } return w +} + +func (w lintStructTagRule) checkTagNameIfNeed(tag *structtag.Tag) (string, bool) { + isUnnamedTag := tag.Name == "" || tag.Name == "-" + if isUnnamedTag { + return "", true + } + + needsToCheckTagName := tag.Key == "bson" || + tag.Key == "json" || + tag.Key == "xml" || + tag.Key == "yaml" || + tag.Key == "protobuf" + + if !needsToCheckTagName { + return "", true + } + + tagName := w.getTagName(tag) + if tagName == "" { + return "", true // No tag name found + } + + // We concat the key and name as the mapping key here + // to allow the same tag name in different tag type. + key := tag.Key + ":" + tagName + if _, ok := w.usedTagName[key]; ok { + return fmt.Sprintf("duplicate tag name: '%s'", tagName), false + } + + w.usedTagName[key] = true + return "", true +} + +func (lintStructTagRule) getTagName(tag *structtag.Tag) string { + switch tag.Key { + case "protobuf": + for _, option := range tag.Options { + if strings.HasPrefix(option, "name=") { + return strings.TrimLeft(option, "name=") + } + } + return "" //protobuf tag lacks 'name' option + default: + return tag.Name + } } // checkTaggedField checks the tag of the given field. @@ -70,6 +118,10 @@ func (w lintStructTagRule) checkTaggedField(f *ast.Field) { } for _, tag := range tags.Tags() { + if msg, ok := w.checkTagNameIfNeed(tag); !ok { + w.addFailure(f.Tag, msg) + } + switch key := tag.Key; key { case "asn1": msg, ok := w.checkASN1Tag(f.Type, tag) @@ -91,7 +143,10 @@ func (w lintStructTagRule) checkTaggedField(f *ast.Field) { w.addFailure(f.Tag, msg) } case "protobuf": - // Not implemented yet + msg, ok := w.checkProtobufTag(tag) + if !ok { + w.addFailure(f.Tag, msg) + } case "required": if tag.Name != "true" && tag.Name != "false" { w.addFailure(f.Tag, "required should be 'true' or 'false'") @@ -122,10 +177,14 @@ func (w lintStructTagRule) checkASN1Tag(t ast.Expr, tag *structtag.Tag) (string, if strings.HasPrefix(opt, "tag:") { parts := strings.Split(opt, ":") tagNumber := parts[1] - if w.usedTagNbr[tagNumber] { - return fmt.Sprintf("duplicated tag number %s", tagNumber), false + number, err := strconv.Atoi(tagNumber) + if err != nil { + return fmt.Sprintf("ASN1 tag must be a number, got '%s'", tagNumber), false + } + if w.usedTagNbr[number] { + return fmt.Sprintf("duplicated tag number %v", number), false } - w.usedTagNbr[tagNumber] = true + w.usedTagNbr[number] = true continue } @@ -149,7 +208,7 @@ func (w lintStructTagRule) checkASN1Tag(t ast.Expr, tag *structtag.Tag) (string, return "", true } -func (w lintStructTagRule) checkBSONTag(options []string) (string, bool) { +func (lintStructTagRule) checkBSONTag(options []string) (string, bool) { for _, opt := range options { switch opt { case "inline", "minsize", "omitempty": @@ -161,14 +220,14 @@ func (w lintStructTagRule) checkBSONTag(options []string) (string, bool) { return "", true } -func (w lintStructTagRule) checkJSONTag(name string, options []string) (string, bool) { +func (lintStructTagRule) checkJSONTag(name string, options []string) (string, bool) { for _, opt := range options { switch opt { case "omitempty", "string": case "": // special case for JSON key "-" if name != "-" { - return "option can not be empty in JSON tag", false + return "option can not be empty in JSON tag", false } default: return fmt.Sprintf("unknown option '%s' in JSON tag", opt), false @@ -178,7 +237,7 @@ func (w lintStructTagRule) checkJSONTag(name string, options []string) (string, return "", true } -func (w lintStructTagRule) checkXMLTag(options []string) (string, bool) { +func (lintStructTagRule) checkXMLTag(options []string) (string, bool) { for _, opt := range options { switch opt { case "any", "attr", "cdata", "chardata", "comment", "innerxml", "omitempty", "typeattr": @@ -190,7 +249,7 @@ func (w lintStructTagRule) checkXMLTag(options []string) (string, bool) { return "", true } -func (w lintStructTagRule) checkYAMLTag(options []string) (string, bool) { +func (lintStructTagRule) checkYAMLTag(options []string) (string, bool) { for _, opt := range options { switch opt { case "flow", "inline", "omitempty": @@ -202,7 +261,7 @@ func (w lintStructTagRule) checkYAMLTag(options []string) (string, bool) { return "", true } -func (w lintStructTagRule) typeValueMatch(t ast.Expr, val string) bool { +func (lintStructTagRule) typeValueMatch(t ast.Expr, val string) bool { tID, ok := t.(*ast.Ident) if !ok { return true @@ -227,6 +286,57 @@ func (w lintStructTagRule) typeValueMatch(t ast.Expr, val string) bool { return typeMatches } +func (w lintStructTagRule) checkProtobufTag(tag *structtag.Tag) (string, bool) { + // check name + switch tag.Name { + case "bytes", "fixed32", "fixed64", "group", "varint", "zigzag32", "zigzag64": + // do nothing + default: + return fmt.Sprintf("invalid protobuf tag name '%s'", tag.Name), false + } + + // check options + seenOptions := map[string]bool{} + for _, opt := range tag.Options { + if number, err := strconv.Atoi(opt); err == nil { + _, alreadySeen := w.usedTagNbr[number] + if alreadySeen { + return fmt.Sprintf("duplicated tag number %v", number), false + } + w.usedTagNbr[number] = true + continue // option is an integer + } + + switch { + case opt == "opt" || opt == "proto3" || opt == "rep" || opt == "req": + // do nothing + case strings.Contains(opt, "="): + o := strings.Split(opt, "=")[0] + _, alreadySeen := seenOptions[o] + if alreadySeen { + return fmt.Sprintf("protobuf tag has duplicated option '%s'", o), false + } + seenOptions[o] = true + continue + } + } + _, hasName := seenOptions["name"] + if !hasName { + return "protobuf tag lacks mandatory option 'name'", false + } + + for k := range seenOptions { + switch k { + case "name", "json": + // do nothing + default: + return fmt.Sprintf("unknown option '%s' in protobuf tag", k), false + } + } + + return "", true +} + func (w lintStructTagRule) addFailure(n ast.Node, msg string) { w.onFailure(lint.Failure{ Node: n, diff --git a/vendor/github.com/mgechev/revive/rule/superfluous-else.go b/vendor/github.com/mgechev/revive/rule/superfluous-else.go index c29be9e0d..a9e4380c9 100644 --- a/vendor/github.com/mgechev/revive/rule/superfluous-else.go +++ b/vendor/github.com/mgechev/revive/rule/superfluous-else.go @@ -12,15 +12,15 @@ import ( type SuperfluousElseRule struct{} // Apply applies the rule to given file. -func (r *SuperfluousElseRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*SuperfluousElseRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) } - var branchingFunctions = map[string]map[string]bool{ - "os": map[string]bool{"Exit": true}, - "log": map[string]bool{ + branchingFunctions := map[string]map[string]bool{ + "os": {"Exit": true}, + "log": { "Fatal": true, "Fatalf": true, "Fatalln": true, @@ -36,7 +36,7 @@ func (r *SuperfluousElseRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa } // Name returns the rule name. -func (r *SuperfluousElseRule) Name() string { +func (*SuperfluousElseRule) Name() string { return "superfluous-else" } @@ -82,9 +82,9 @@ func (w lintSuperfluousElse) Visit(node ast.Node) ast.Visitor { lastStmt := ifStmt.Body.List[len(ifStmt.Body.List)-1] switch stmt := lastStmt.(type) { case *ast.BranchStmt: - token := stmt.Tok.String() - if token != "fallthrough" { - w.onFailure(newFailure(ifStmt.Else, "if block ends with a "+token+" statement, so drop this else and outdent its block"+extra)) + tok := stmt.Tok.String() + if tok != "fallthrough" { + w.onFailure(newFailure(ifStmt.Else, "if block ends with a "+tok+" statement, so drop this else and outdent its block"+extra)) } case *ast.ExprStmt: if ce, ok := stmt.X.(*ast.CallExpr); ok { // it's a function call diff --git a/vendor/github.com/mgechev/revive/rule/time-equal.go b/vendor/github.com/mgechev/revive/rule/time-equal.go new file mode 100644 index 000000000..72ecf26fe --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/time-equal.go @@ -0,0 +1,76 @@ +package rule + +import ( + "fmt" + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// TimeEqualRule shows where "==" and "!=" used for equality check time.Time +type TimeEqualRule struct{} + +// Apply applies the rule to given file. +func (*TimeEqualRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := &lintTimeEqual{file, onFailure} + if w.file.Pkg.TypeCheck() != nil { + return nil + } + + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (*TimeEqualRule) Name() string { + return "time-equal" +} + +type lintTimeEqual struct { + file *lint.File + onFailure func(lint.Failure) +} + +func (l *lintTimeEqual) Visit(node ast.Node) ast.Visitor { + expr, ok := node.(*ast.BinaryExpr) + if !ok { + return l + } + + switch expr.Op { + case token.EQL, token.NEQ: + default: + return l + } + + xtyp := l.file.Pkg.TypeOf(expr.X) + ytyp := l.file.Pkg.TypeOf(expr.Y) + + if !isNamedType(xtyp, "time", "Time") || !isNamedType(ytyp, "time", "Time") { + return l + } + + var failure string + switch expr.Op { + case token.EQL: + failure = fmt.Sprintf("use %s.Equal(%s) instead of %q operator", expr.X, expr.Y, expr.Op) + case token.NEQ: + failure = fmt.Sprintf("use !%s.Equal(%s) instead of %q operator", expr.X, expr.Y, expr.Op) + } + + l.onFailure(lint.Failure{ + Category: "time", + Confidence: 1, + Node: node, + Failure: failure, + }) + + return l +} diff --git a/vendor/github.com/mgechev/revive/rule/time-naming.go b/vendor/github.com/mgechev/revive/rule/time-naming.go index a93f4b5ae..cea452e61 100644 --- a/vendor/github.com/mgechev/revive/rule/time-naming.go +++ b/vendor/github.com/mgechev/revive/rule/time-naming.go @@ -13,7 +13,7 @@ import ( type TimeNamingRule struct{} // Apply applies the rule to given file. -func (r *TimeNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*TimeNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -28,7 +28,7 @@ func (r *TimeNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure } // Name returns the rule name. -func (r *TimeNamingRule) Name() string { +func (*TimeNamingRule) Name() string { return "time-naming" } @@ -76,10 +76,12 @@ func (w *lintTimeNames) Visit(node ast.Node) ast.Visitor { // timeSuffixes is a list of name suffixes that imply a time unit. // This is not an exhaustive list. var timeSuffixes = []string{ - "Sec", "Secs", "Seconds", + "Hour", "Hours", + "Min", "Mins", "Minutes", "Minute", + "Sec", "Secs", "Seconds", "Second", "Msec", "Msecs", - "Milli", "Millis", "Milliseconds", - "Usec", "Usecs", "Microseconds", + "Milli", "Millis", "Milliseconds", "Millisecond", + "Usec", "Usecs", "Microseconds", "Microsecond", "MS", "Ms", } diff --git a/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go b/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go index c06626b5a..f0e83b0ce 100644 --- a/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go +++ b/vendor/github.com/mgechev/revive/rule/unconditional-recursion.go @@ -10,7 +10,7 @@ import ( type UnconditionalRecursionRule struct{} // Apply applies the rule to given file. -func (r *UnconditionalRecursionRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*UnconditionalRecursionRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -23,7 +23,7 @@ func (r *UnconditionalRecursionRule) Apply(file *lint.File, _ lint.Arguments) [] } // Name returns the rule name. -func (r *UnconditionalRecursionRule) Name() string { +func (*UnconditionalRecursionRule) Name() string { return "unconditional-recursion" } @@ -61,8 +61,10 @@ func (w lintUnconditionalRecursionRule) Visit(node ast.Node) ast.Visitor { case *ast.FuncDecl: var rec *ast.Ident switch { - case n.Recv == nil || n.Recv.NumFields() < 1 || len(n.Recv.List[0].Names) < 1: + case n.Recv == nil: rec = nil + case n.Recv.NumFields() < 1 || len(n.Recv.List[0].Names) < 1: + rec = &ast.Ident{Name: "_"} default: rec = n.Recv.List[0].Names[0] } @@ -137,9 +139,9 @@ func (w *lintUnconditionalRecursionRule) updateFuncStatus(node ast.Node) { } var exitFunctions = map[string]map[string]bool{ - "os": map[string]bool{"Exit": true}, - "syscall": map[string]bool{"Exit": true}, - "log": map[string]bool{ + "os": {"Exit": true}, + "syscall": {"Exit": true}, + "log": { "Fatal": true, "Fatalf": true, "Fatalln": true, @@ -149,7 +151,7 @@ var exitFunctions = map[string]map[string]bool{ }, } -func (w *lintUnconditionalRecursionRule) hasControlExit(node ast.Node) bool { +func (lintUnconditionalRecursionRule) hasControlExit(node ast.Node) bool { // isExit returns true if the given node makes control exit the function isExit := func(node ast.Node) bool { switch n := node.(type) { diff --git a/vendor/github.com/mgechev/revive/rule/unexported-naming.go b/vendor/github.com/mgechev/revive/rule/unexported-naming.go index 96cec3e46..0c2b39d41 100644 --- a/vendor/github.com/mgechev/revive/rule/unexported-naming.go +++ b/vendor/github.com/mgechev/revive/rule/unexported-naming.go @@ -12,7 +12,7 @@ import ( type UnexportedNamingRule struct{} // Apply applies the rule to given file. -func (r *UnexportedNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*UnexportedNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) @@ -25,7 +25,7 @@ func (r *UnexportedNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.F } // Name returns the rule name. -func (r *UnexportedNamingRule) Name() string { +func (*UnexportedNamingRule) Name() string { return "unexported-naming" } diff --git a/vendor/github.com/mgechev/revive/rule/unexported-return.go b/vendor/github.com/mgechev/revive/rule/unexported-return.go index c9c8a41d3..10f8e3fbe 100644 --- a/vendor/github.com/mgechev/revive/rule/unexported-return.go +++ b/vendor/github.com/mgechev/revive/rule/unexported-return.go @@ -5,6 +5,7 @@ import ( "go/ast" "go/types" + "github.com/mgechev/revive/internal/typeparams" "github.com/mgechev/revive/lint" ) @@ -12,7 +13,7 @@ import ( type UnexportedReturnRule struct{} // Apply applies the rule to given file. -func (r *UnexportedReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*UnexportedReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -31,7 +32,7 @@ func (r *UnexportedReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.F } // Name returns the rule name. -func (r *UnexportedReturnRule) Name() string { +func (*UnexportedReturnRule) Name() string { return "unexported-return" } @@ -55,7 +56,7 @@ func (w lintUnexportedReturn) Visit(n ast.Node) ast.Visitor { thing := "func" if fn.Recv != nil && len(fn.Recv.List) > 0 { thing = "method" - if !ast.IsExported(receiverType(fn)) { + if !ast.IsExported(typeparams.ReceiverType(fn)) { // Don't report exported methods of unexported types, // such as private implementations of sort.Interface. return nil @@ -82,24 +83,24 @@ func (w lintUnexportedReturn) Visit(n ast.Node) ast.Visitor { // It is imprecise, and will err on the side of returning true, // such as for composite types. func exportedType(typ types.Type) bool { - switch T := typ.(type) { + switch t := typ.(type) { case *types.Named: - obj := T.Obj() + obj := t.Obj() switch { // Builtin types have no package. case obj.Pkg() == nil: case obj.Exported(): default: - _, ok := T.Underlying().(*types.Interface) + _, ok := t.Underlying().(*types.Interface) return ok } return true case *types.Map: - return exportedType(T.Key()) && exportedType(T.Elem()) + return exportedType(t.Key()) && exportedType(t.Elem()) case interface { Elem() types.Type }: // array, slice, pointer, chan - return exportedType(T.Elem()) + return exportedType(t.Elem()) } // Be conservative about other types, such as struct, interface, etc. return true diff --git a/vendor/github.com/mgechev/revive/rule/unhandled-error.go b/vendor/github.com/mgechev/revive/rule/unhandled-error.go index 0e2f62875..6cde24b7f 100644 --- a/vendor/github.com/mgechev/revive/rule/unhandled-error.go +++ b/vendor/github.com/mgechev/revive/rule/unhandled-error.go @@ -4,32 +4,44 @@ import ( "fmt" "go/ast" "go/types" + "sync" "github.com/mgechev/revive/lint" ) // UnhandledErrorRule lints given else constructs. -type UnhandledErrorRule struct{} +type UnhandledErrorRule struct { + ignoreList ignoreListType + sync.Mutex +} type ignoreListType map[string]struct{} -// Apply applies the rule to given file. -func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { - var failures []lint.Failure +func (r *UnhandledErrorRule) configure(arguments lint.Arguments) { + r.Lock() + if r.ignoreList == nil { + r.ignoreList = make(ignoreListType, len(arguments)) - ignoreList := make(ignoreListType, len(args)) + for _, arg := range arguments { + argStr, ok := arg.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the unhandled-error rule. Expecting a string, got %T", arg)) + } - for _, arg := range args { - argStr, ok := arg.(string) - if !ok { - panic(fmt.Sprintf("Invalid argument to the unhandled-error rule. Expecting a string, got %T", arg)) + r.ignoreList[argStr] = struct{}{} } - - ignoreList[argStr] = struct{}{} } + r.Unlock() +} + +// Apply applies the rule to given file. +func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure { + r.configure(args) + + var failures []lint.Failure walker := &lintUnhandledErrors{ - ignoreList: ignoreList, + ignoreList: r.ignoreList, pkg: file.Pkg, onFailure: func(failure lint.Failure) { failures = append(failures, failure) @@ -43,7 +55,7 @@ func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint. } // Name returns the rule name. -func (r *UnhandledErrorRule) Name() string { +func (*UnhandledErrorRule) Name() string { return "unhandled-error" } diff --git a/vendor/github.com/mgechev/revive/rule/unnecessary-stmt.go b/vendor/github.com/mgechev/revive/rule/unnecessary-stmt.go index 732d8a8bb..8e0784ba4 100644 --- a/vendor/github.com/mgechev/revive/rule/unnecessary-stmt.go +++ b/vendor/github.com/mgechev/revive/rule/unnecessary-stmt.go @@ -11,7 +11,7 @@ import ( type UnnecessaryStmtRule struct{} // Apply applies the rule to given file. -func (r *UnnecessaryStmtRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*UnnecessaryStmtRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) @@ -23,7 +23,7 @@ func (r *UnnecessaryStmtRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa } // Name returns the rule name. -func (r *UnnecessaryStmtRule) Name() string { +func (*UnnecessaryStmtRule) Name() string { return "unnecessary-stmt" } diff --git a/vendor/github.com/mgechev/revive/rule/unreachable-code.go b/vendor/github.com/mgechev/revive/rule/unreachable-code.go index c81e9e733..dcc5b7905 100644 --- a/vendor/github.com/mgechev/revive/rule/unreachable-code.go +++ b/vendor/github.com/mgechev/revive/rule/unreachable-code.go @@ -10,15 +10,20 @@ import ( type UnreachableCodeRule struct{} // Apply applies the rule to given file. -func (r *UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) } - var branchingFunctions = map[string]map[string]bool{ - "os": map[string]bool{"Exit": true}, - "log": map[string]bool{ + testingFunctions := map[string]bool{ + "Fatal": true, + "Fatalf": true, + "FailNow": true, + } + branchingFunctions := map[string]map[string]bool{ + "os": {"Exit": true}, + "log": { "Fatal": true, "Fatalf": true, "Fatalln": true, @@ -26,6 +31,9 @@ func (r *UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa "Panicf": true, "Panicln": true, }, + "t": testingFunctions, + "b": testingFunctions, + "f": testingFunctions, } w := lintUnreachableCode{onFailure, branchingFunctions} @@ -34,7 +42,7 @@ func (r *UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa } // Name returns the rule name. -func (r *UnreachableCodeRule) Name() string { +func (*UnreachableCodeRule) Name() string { return "unreachable-code" } diff --git a/vendor/github.com/mgechev/revive/rule/unused-param.go b/vendor/github.com/mgechev/revive/rule/unused-param.go index 60df908d3..ab3da453e 100644 --- a/vendor/github.com/mgechev/revive/rule/unused-param.go +++ b/vendor/github.com/mgechev/revive/rule/unused-param.go @@ -11,7 +11,7 @@ import ( type UnusedParamRule struct{} // Apply applies the rule to given file. -func (r *UnusedParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*UnusedParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -26,7 +26,7 @@ func (r *UnusedParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failur } // Name returns the rule name. -func (r *UnusedParamRule) Name() string { +func (*UnusedParamRule) Name() string { return "unused-parameter" } diff --git a/vendor/github.com/mgechev/revive/rule/use-any.go b/vendor/github.com/mgechev/revive/rule/use-any.go new file mode 100644 index 000000000..bdf3c936d --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/use-any.go @@ -0,0 +1,54 @@ +package rule + +import ( + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// UseAnyRule lints given else constructs. +type UseAnyRule struct{} + +// Apply applies the rule to given file. +func (*UseAnyRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + walker := lintUseAny{ + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + } + fileAst := file.AST + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (*UseAnyRule) Name() string { + return "use-any" +} + +type lintUseAny struct { + onFailure func(lint.Failure) +} + +func (w lintUseAny) Visit(n ast.Node) ast.Visitor { + it, ok := n.(*ast.InterfaceType) + if !ok { + return w + } + + if len(it.Methods.List) != 0 { + return w // it is not and empty interface + } + + w.onFailure(lint.Failure{ + Node: n, + Confidence: 1, + Category: "naming", + Failure: "since GO 1.18 'interface{}' can be replaced by 'any'", + }) + + return w +} diff --git a/vendor/github.com/mgechev/revive/rule/useless-break.go b/vendor/github.com/mgechev/revive/rule/useless-break.go new file mode 100644 index 000000000..8db20c9b8 --- /dev/null +++ b/vendor/github.com/mgechev/revive/rule/useless-break.go @@ -0,0 +1,82 @@ +package rule + +import ( + "go/ast" + "go/token" + + "github.com/mgechev/revive/lint" +) + +// UselessBreak lint rule. +type UselessBreak struct{} + +// Apply applies the rule to given file. +func (*UselessBreak) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + astFile := file.AST + w := &lintUselessBreak{onFailure, false} + ast.Walk(w, astFile) + return failures +} + +// Name returns the rule name. +func (*UselessBreak) Name() string { + return "useless-break" +} + +type lintUselessBreak struct { + onFailure func(lint.Failure) + inLoopBody bool +} + +func (w *lintUselessBreak) Visit(node ast.Node) ast.Visitor { + switch v := node.(type) { + case *ast.ForStmt: + w.inLoopBody = true + ast.Walk(w, v.Body) + w.inLoopBody = false + return nil + case *ast.RangeStmt: + w.inLoopBody = true + ast.Walk(w, v.Body) + w.inLoopBody = false + return nil + case *ast.CommClause: + for _, n := range v.Body { + w.inspectCaseStatement(n) + } + return nil + case *ast.CaseClause: + for _, n := range v.Body { + w.inspectCaseStatement(n) + } + return nil + } + return w +} + +func (w *lintUselessBreak) inspectCaseStatement(n ast.Stmt) { + switch s := n.(type) { + case *ast.BranchStmt: + if s.Tok != token.BREAK { + return // not a break statement + } + if s.Label != nil { + return // labeled break statement, usually affects a nesting loop + } + msg := "useless break in case clause" + if w.inLoopBody { + msg += " (WARN: this break statement affects this switch or select statement and not the loop enclosing it)" + } + w.onFailure(lint.Failure{ + Confidence: 1, + Node: s, + Failure: msg, + }) + } +} diff --git a/vendor/github.com/mgechev/revive/rule/utils.go b/vendor/github.com/mgechev/revive/rule/utils.go index 38677c839..dca1674ca 100644 --- a/vendor/github.com/mgechev/revive/rule/utils.go +++ b/vendor/github.com/mgechev/revive/rule/utils.go @@ -13,35 +13,17 @@ import ( "github.com/mgechev/revive/lint" ) -const styleGuideBase = "https://golang.org/wiki/CodeReviewComments" - // isBlank returns whether id is the blank identifier "_". // If id == nil, the answer is false. func isBlank(id *ast.Ident) bool { return id != nil && id.Name == "_" } -func isTest(f *lint.File) bool { - return strings.HasSuffix(f.Name, "_test.go") -} - var commonMethods = map[string]bool{ "Error": true, "Read": true, "ServeHTTP": true, "String": true, "Write": true, -} - -func receiverType(fn *ast.FuncDecl) string { - switch e := fn.Recv.List[0].Type.(type) { - case *ast.Ident: - return e.Name - case *ast.StarExpr: - if id, ok := e.X.(*ast.Ident); ok { - return id.Name - } - } - // The parser accepts much more than just the legal forms. - return "invalid-type" + "Unwrap": true, } var knownNameExceptions = map[string]bool{ @@ -85,12 +67,13 @@ var zeroLiteral = map[string]bool{ "0i": true, } -func validType(T types.Type) bool { - return T != nil && - T != types.Typ[types.Invalid] && - !strings.Contains(T.String(), "invalid type") // good but not foolproof +func validType(t types.Type) bool { + return t != nil && + t != types.Typ[types.Invalid] && + !strings.Contains(t.String(), "invalid type") // good but not foolproof } +// isPkgDot checks if the expression is . func isPkgDot(expr ast.Expr, pkg, name string) bool { sel, ok := expr.(*ast.SelectorExpr) return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name) @@ -110,7 +93,7 @@ func srcLine(src []byte, p token.Position) string { // pick yields a list of nodes by picking them from a sub-ast with root node n. // Nodes are selected by applying the fselect function -// f function is applied to each selected node before inseting it in the final result. +// f function is applied to each selected node before inserting it in the final result. // If f==nil then it defaults to the identity function (ie it returns the node itself) func pick(n ast.Node, fselect func(n ast.Node) bool, f func(n ast.Node) []ast.Node) []ast.Node { var result []ast.Node @@ -131,14 +114,6 @@ func pick(n ast.Node, fselect func(n ast.Node) bool, f func(n ast.Node) []ast.No return result } -func pickFromExpList(l []ast.Expr, fselect func(n ast.Node) bool, f func(n ast.Node) []ast.Node) []ast.Node { - result := make([]ast.Node, 0) - for _, e := range l { - result = append(result, pick(e, fselect, f)...) - } - return result -} - type picker struct { fselect func(n ast.Node) bool onSelect func(n ast.Node) @@ -189,3 +164,10 @@ func gofmt(x interface{}) string { printer.Fprint(&buf, fs, x) return buf.String() } + +// checkNumberOfArguments fails if the given number of arguments is not, at least, the expected one +func checkNumberOfArguments(expected int, args lint.Arguments, ruleName string) { + if len(args) < expected { + panic(fmt.Sprintf("not enough arguments for %s rule, expected %d, got %d. Please check the rule's documentation", ruleName, expected, len(args))) + } +} diff --git a/vendor/github.com/mgechev/revive/rule/var-declarations.go b/vendor/github.com/mgechev/revive/rule/var-declarations.go index 441132115..a15ff1eb4 100644 --- a/vendor/github.com/mgechev/revive/rule/var-declarations.go +++ b/vendor/github.com/mgechev/revive/rule/var-declarations.go @@ -13,7 +13,7 @@ import ( type VarDeclarationsRule struct{} // Apply applies the rule to given file. -func (r *VarDeclarationsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*VarDeclarationsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure fileAst := file.AST @@ -32,7 +32,7 @@ func (r *VarDeclarationsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Fa } // Name returns the rule name. -func (r *VarDeclarationsRule) Name() string { +func (*VarDeclarationsRule) Name() string { return "var-declaration" } diff --git a/vendor/github.com/mgechev/revive/rule/var-naming.go b/vendor/github.com/mgechev/revive/rule/var-naming.go index 768f65b96..3c0c19cdf 100644 --- a/vendor/github.com/mgechev/revive/rule/var-naming.go +++ b/vendor/github.com/mgechev/revive/rule/var-naming.go @@ -5,34 +5,47 @@ import ( "go/ast" "go/token" "strings" + "sync" "github.com/mgechev/revive/lint" ) // VarNamingRule lints given else constructs. -type VarNamingRule struct{} - -// Apply applies the rule to given file. -func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { - var failures []lint.Failure +type VarNamingRule struct { + configured bool + whitelist []string + blacklist []string + sync.Mutex +} - var whitelist []string - var blacklist []string +func (r *VarNamingRule) configure(arguments lint.Arguments) { + r.Lock() + if !r.configured { + if len(arguments) >= 1 { + r.whitelist = getList(arguments[0], "whitelist") + } - if len(arguments) >= 1 { - whitelist = getList(arguments[0], "whitelist") + if len(arguments) >= 2 { + r.blacklist = getList(arguments[1], "blacklist") + } + r.configured = true } + r.Unlock() +} - if len(arguments) >= 2 { - blacklist = getList(arguments[1], "blacklist") - } +// Apply applies the rule to given file. +func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) + + var failures []lint.Failure fileAst := file.AST + walker := lintNames{ file: file, fileAst: fileAst, - whitelist: whitelist, - blacklist: blacklist, + whitelist: r.whitelist, + blacklist: r.blacklist, onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, @@ -43,7 +56,7 @@ func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint. walker.onFailure(lint.Failure{ Failure: "don't use an underscore in package name", Confidence: 1, - Node: walker.fileAst, + Node: walker.fileAst.Name, Category: "naming", }) } @@ -54,7 +67,7 @@ func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint. } // Name returns the rule name. -func (r *VarNamingRule) Name() string { +func (*VarNamingRule) Name() string { return "var-naming" } @@ -120,13 +133,11 @@ func check(id *ast.Ident, thing string, w *lintNames) { } type lintNames struct { - file *lint.File - fileAst *ast.File - lastGen *ast.GenDecl - genDeclMissingComments map[*ast.GenDecl]bool - onFailure func(lint.Failure) - whitelist []string - blacklist []string + file *lint.File + fileAst *ast.File + onFailure func(lint.Failure) + whitelist []string + blacklist []string } func (w *lintNames) Visit(n ast.Node) ast.Visitor { @@ -141,7 +152,12 @@ func (w *lintNames) Visit(n ast.Node) ast.Visitor { } } case *ast.FuncDecl: - if w.file.IsTest() && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) { + funcName := v.Name.Name + if w.file.IsTest() && + (strings.HasPrefix(funcName, "Example") || + strings.HasPrefix(funcName, "Test") || + strings.HasPrefix(funcName, "Benchmark") || + strings.HasPrefix(funcName, "Fuzz")) { return w } @@ -184,7 +200,7 @@ func (w *lintNames) Visit(n ast.Node) ast.Visitor { } case *ast.InterfaceType: // Do not check interface method names. - // They are often constrainted by the method names of concrete types. + // They are often constrained by the method names of concrete types. for _, x := range v.Methods.List { ft, ok := x.Type.(*ast.FuncType) if !ok { // might be an embedded interface name diff --git a/vendor/github.com/mgechev/revive/rule/waitgroup-by-value.go b/vendor/github.com/mgechev/revive/rule/waitgroup-by-value.go index b86929136..98644f41c 100644 --- a/vendor/github.com/mgechev/revive/rule/waitgroup-by-value.go +++ b/vendor/github.com/mgechev/revive/rule/waitgroup-by-value.go @@ -10,7 +10,7 @@ import ( type WaitGroupByValueRule struct{} // Apply applies the rule to given file. -func (r *WaitGroupByValueRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { +func (*WaitGroupByValueRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { @@ -23,7 +23,7 @@ func (r *WaitGroupByValueRule) Apply(file *lint.File, _ lint.Arguments) []lint.F } // Name returns the rule name. -func (r *WaitGroupByValueRule) Name() string { +func (*WaitGroupByValueRule) Name() string { return "waitgroup-by-value" } -- cgit mrf-deployment