aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/nunnatsa
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2023-02-22 22:16:50 +0100
committerTaras Madan <tarasmadan@google.com>2023-02-24 12:47:23 +0100
commit4165372ec8fd142475a4e35fd0cf4f8042132208 (patch)
tree21cd62211b4dd80bee469054c5b65db77342333c /vendor/github.com/nunnatsa
parent2b3ed821a493b8936c8bacfa6f8b4f1c90a00855 (diff)
dependencies: update
set go min requirements to 1.19 update dependencies update vendor
Diffstat (limited to 'vendor/github.com/nunnatsa')
-rw-r--r--vendor/github.com/nunnatsa/ginkgolinter/.gitignore2
-rw-r--r--vendor/github.com/nunnatsa/ginkgolinter/LICENSE21
-rw-r--r--vendor/github.com/nunnatsa/ginkgolinter/Makefile14
-rw-r--r--vendor/github.com/nunnatsa/ginkgolinter/README.md181
-rw-r--r--vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go662
-rw-r--r--vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go167
-rw-r--r--vendor/github.com/nunnatsa/ginkgolinter/reverseassertion/reverse_assertion.go17
-rw-r--r--vendor/github.com/nunnatsa/ginkgolinter/types/boolean.go32
-rw-r--r--vendor/github.com/nunnatsa/ginkgolinter/types/suppress.go67
9 files changed, 1163 insertions, 0 deletions
diff --git a/vendor/github.com/nunnatsa/ginkgolinter/.gitignore b/vendor/github.com/nunnatsa/ginkgolinter/.gitignore
new file mode 100644
index 000000000..7d7f8b10c
--- /dev/null
+++ b/vendor/github.com/nunnatsa/ginkgolinter/.gitignore
@@ -0,0 +1,2 @@
+ginkgolinter
+bin/
diff --git a/vendor/github.com/nunnatsa/ginkgolinter/LICENSE b/vendor/github.com/nunnatsa/ginkgolinter/LICENSE
new file mode 100644
index 000000000..11096c5c8
--- /dev/null
+++ b/vendor/github.com/nunnatsa/ginkgolinter/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Nahshon Unna Tsameret
+
+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/nunnatsa/ginkgolinter/Makefile b/vendor/github.com/nunnatsa/ginkgolinter/Makefile
new file mode 100644
index 000000000..f37079839
--- /dev/null
+++ b/vendor/github.com/nunnatsa/ginkgolinter/Makefile
@@ -0,0 +1,14 @@
+build:
+ go build -o ginkgolinter ./cmd/ginkgolinter
+
+build-for-windows:
+ GOOS=windows GOARCH=amd64 go build -o bin/ginkgolinter-amd64.exe ./cmd/ginkgolinter
+
+build-for-mac:
+ GOOS=darwin GOARCH=amd64 go build -o bin/ginkgolinter-amd64-darwin ./cmd/ginkgolinter
+
+build-for-linux:
+ GOOS=linux GOARCH=amd64 go build -o bin/ginkgolinter-amd64-linux ./cmd/ginkgolinter
+ GOOS=linux GOARCH=386 go build -o bin/ginkgolinter-386-linux ./cmd/ginkgolinter
+
+build-all: build build-for-linux build-for-mac build-for-windows
diff --git a/vendor/github.com/nunnatsa/ginkgolinter/README.md b/vendor/github.com/nunnatsa/ginkgolinter/README.md
new file mode 100644
index 000000000..fa31117af
--- /dev/null
+++ b/vendor/github.com/nunnatsa/ginkgolinter/README.md
@@ -0,0 +1,181 @@
+[![Go Report Card](https://goreportcard.com/badge/github.com/nunnatsa/ginkgolinter)](https://goreportcard.com/report/github.com/nunnatsa/ginkgolinter)
+[![Coverage Status](https://coveralls.io/repos/github/nunnatsa/ginkgolinter/badge.svg?branch=main)](https://coveralls.io/github/nunnatsa/ginkgolinter?branch=main)
+# ginkgo-linter
+[ginkgo](https://onsi.github.io/ginkgo/) is a popular testing framework and [gomega](https://onsi.github.io/gomega/) is its assertion package.
+
+This is a golang linter to enforce some standards while using the ginkgo and gomega packages.
+
+## Install the CLI
+Download the right executable from the latest release, according to your OS.
+
+Another option is to use go:
+```shell
+go install github.com/nunnatsa/ginkgolinter/cmd/ginkgolinter@latest
+```
+Then add the new executable to your PATH.
+
+## usage
+```shell
+ginkgolinter [-fix] ./...
+```
+Use the `-fix` flag to apply the fix suggestions to the source code.
+
+## Linter Checks
+The linter checks the gomega assertions in golang test code. Gomega may be used together with ginkgo tests, For example:
+```go
+It("should test something", func() { // It is ginkgo test case function
+ Expect("abcd").To(HaveLen(4), "the string should have a length of 4") // Expect is the gomega assertion
+})
+```
+or within a classic golang test code, like this:
+```go
+func TestWithGomega(t *testing.T) {
+ g := NewWithT(t)
+ g.Expect("abcd").To(HaveLen(4), "the string should have a length of 4")
+}
+```
+
+In some cases, the gomega will be passed as a variable to function by ginkgo, for example:
+```go
+Eventually(func(g Gomega) error {
+ g.Expect("abcd").To(HaveLen(4), "the string should have a length of 4")
+ return nil
+}).Should(Succeed())
+```
+
+The linter checks the `Expect`, `ExpectWithOffset` and the `Ω` "actual" functions, with the `Should`, `ShouldNot`, `To`, `ToNot` and `NotTo` assertion functions.
+
+It also supports the embedded `Not()` matcher
+
+### Wrong Length Assertion
+The linter finds assertion of the golang built-in `len` function, with all kind of matchers, while there are already gomega matchers for these usecases; We want to assert the item, rather than its length.
+
+There are several wrong patterns:
+```go
+Expect(len(x)).To(Equal(0)) // should be: Expect(x).To(BeEmpty())
+Expect(len(x)).To(BeZero()) // should be: Expect(x).To(BeEmpty())
+Expect(len(x)).To(BeNumeric(">", 0)) // should be: Expect(x).ToNot(BeEmpty())
+Expect(len(x)).To(BeNumeric(">=", 1)) // should be: Expect(x).ToNot(BeEmpty())
+Expect(len(x)).To(BeNumeric("==", 0)) // should be: Expect(x).To(BeEmpty())
+Expect(len(x)).To(BeNumeric("!=", 0)) // should be: Expect(x).ToNot(BeEmpty())
+
+Expect(len(x)).To(Equal(1)) // should be: Expect(x).To(HaveLen(1))
+Expect(len(x)).To(BeNumeric("==", 2)) // should be: Expect(x).To(HaveLen(2))
+Expect(len(x)).To(BeNumeric("!=", 3)) // should be: Expect(x).ToNot(HaveLen(3))
+```
+
+It also supports the embedded `Not()` matcher; e.g.
+
+`Ω(len(x)).Should(Not(Equal(4)))` => `Ω(x).ShouldNot(HaveLen(4))`
+
+Or even (double negative):
+
+`Ω(len(x)).To(Not(BeNumeric(">", 0)))` => `Ω(x).To(BeEmpty())`
+
+The output of the linter,when finding issues, looks like this:
+```
+./testdata/src/a/a.go:14:5: ginkgo-linter: wrong length assertion; consider using `Expect("abcd").Should(HaveLen(4))` instead
+./testdata/src/a/a.go:18:5: ginkgo-linter: wrong length assertion; consider using `Expect("").Should(BeEmpty())` instead
+./testdata/src/a/a.go:22:5: ginkgo-linter: wrong length assertion; consider using `Expect("").Should(BeEmpty())` instead
+```
+
+### Wrong `nil` Assertion
+The linter finds assertion of the comparison to nil, with all kind of matchers, instead of using the existing `BeNil()` matcher; We want to assert the item, rather than a comparison result.
+
+There are several wrong patterns:
+
+```go
+Expect(x == nil).To(Equal(true)) // should be: Expect(x).To(BeNil())
+Expect(nil == x).To(Equal(true)) // should be: Expect(x).To(BeNil())
+Expect(x != nil).To(Equal(true)) // should be: Expect(x).ToNot(BeNil())
+Expect(nil != nil).To(Equal(true)) // should be: Expect(x).ToNot(BeNil())
+
+Expect(x == nil).To(BeTrue()) // should be: Expect(x).To(BeNil())
+Expect(x == nil).To(BeFalse()) // should be: Expect(x).ToNot(BeNil())
+```
+It also supports the embedded `Not()` matcher; e.g.
+
+`Ω(x == nil).Should(Not(BeTrue()))` => `Ω(x).ShouldNot(BeNil())`
+
+Or even (double negative):
+
+`Ω(x != nil).Should(Not(BeTrue()))` => `Ω(x).Should(BeNil())`
+
+### Wrong boolean Assertion
+The linter finds assertion using the `Equal` method, with the values of to `true` or `false`, instead
+of using the existing `BeTrue()` or `BeFalse()` matcher.
+
+There are several wrong patterns:
+
+```go
+Expect(x).To(Equal(true)) // should be: Expect(x).To(BeTrue())
+Expect(x).To(Equal(false)) // should be: Expect(x).To(BeFalse())
+```
+It also supports the embedded `Not()` matcher; e.g.
+
+`Ω(x).Should(Not(Equal(True)))` => `Ω(x).ShouldNot(BeBeTrue())`
+
+### Wrong Error Assertion
+The linter finds assertion of errors compared with nil, or to be equal nil, or to be nil. The linter suggests to use `Succeed` for functions or `HaveOccurred` for error values..
+
+There are several wrong patterns:
+
+```go
+Expect(err).To(BeNil()) // should be: Expect(err).ToNot(HaveOccurred())
+Expect(err == nil).To(Equal(true)) // should be: Expect(err).ToNot(HaveOccurred())
+Expect(err == nil).To(BeFalse()) // should be: Expect(err).To(HaveOccurred())
+Expect(err != nil).To(BeTrue()) // should be: Expect(err).To(HaveOccurred())
+Expect(funcReturnsError()).To(BeNil()) // should be: Expect(funcReturnsError()).To(Succeed())
+
+and so on
+```
+It also supports the embedded `Not()` matcher; e.g.
+
+`Ω(err == nil).Should(Not(BeTrue()))` => `Ω(x).Should(HaveOccurred())`
+
+## Suppress the linter
+### Suppress warning from command line
+* Use the `--suppress-len-assertion=true` flag to suppress the wrong length assertion warning
+* Use the `--suppress-nil-assertion=true` flag to suppress the wrong nil assertion warning
+* Use the `--suppress-err-assertion=true` flag to suppress the wrong error assertion warning
+
+### Suppress warning from the code
+To suppress the wrong length assertion warning, add a comment with (only)
+
+`ginkgo-linter:ignore-len-assert-warning`.
+
+To suppress the wrong nil assertion warning, add a comment with (only)
+
+`ginkgo-linter:ignore-nil-assert-warning`.
+
+To suppress the wrong error assertion warning, add a comment with (only)
+
+`ginkgo-linter:ignore-err-assert-warning`.
+
+There are two options to use these comments:
+1. If the comment is at the top of the file, supress the warning for the whole file; e.g.:
+ ```go
+ package mypackage
+
+ // ginkgo-linter:ignore-len-assert-warning
+
+ import (
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ )
+
+ var _ = Describe("my test", func() {
+ It("should do something", func() {
+ Expect(len("abc")).Should(Equal(3)) // nothing in this file will trigger the warning
+ })
+ })
+ ```
+
+2. If the comment is before a wrong length check expression, the warning is suppressed for this expression only; for example:
+ ```golang
+ It("should test something", func() {
+ // ginkgo-linter:ignore-nil-assert-warning
+ Expect(x == nil).Should(BeTrue()) // this line will not trigger the warning
+ Expect(x == nil).Should(BeTrue()) // this line will trigger the warning
+ }
+ ```
diff --git a/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go b/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go
new file mode 100644
index 000000000..fc089be10
--- /dev/null
+++ b/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go
@@ -0,0 +1,662 @@
+package ginkgolinter
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/printer"
+ "go/token"
+ gotypes "go/types"
+
+ "github.com/go-toolsmith/astcopy"
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/nunnatsa/ginkgolinter/gomegahandler"
+ "github.com/nunnatsa/ginkgolinter/reverseassertion"
+ "github.com/nunnatsa/ginkgolinter/types"
+)
+
+// The ginkgolinter enforces standards of using ginkgo and gomega.
+//
+// The current checks are:
+// * enforce right length assertion - warn for assertion of len(something):
+//
+// This check finds the following patterns and suggests an alternative
+// * Expect(len(something)).To(Equal(number)) ===> Expect(x).To(HaveLen(number))
+// * ExpectWithOffset(1, len(something)).ShouldNot(Equal(0)) ===> ExpectWithOffset(1, something).ShouldNot(BeEmpty())
+// * Ω(len(something)).NotTo(BeZero()) ===> Ω(something).NotTo(BeEmpty())
+// * Expect(len(something)).To(BeNumerically(">", 0)) ===> Expect(something).ToNot(BeEmpty())
+// * Expect(len(something)).To(BeNumerically(">=", 1)) ===> Expect(something).ToNot(BeEmpty())
+// * Expect(len(something)).To(BeNumerically("==", number)) ===> Expect(something).To(HaveLen(number))
+//
+// * enforce right nil assertion - warn for assertion of x == nil:
+// This check finds the following patterns and suggests an alternative
+// * Expect(x == nil).Should(Equal(true)) ===> Expect(x).Should(BeNil())
+// * Expect(nil == x).Should(BeTrue()) ===> Expect(x).Should(BeNil())
+// * Expect(x != nil).Should(Equal(false)) ===> Expect(x).Should(BeNil())
+// * Expect(nil == x).Should(BeFalse()) ===> Expect(x).Should(BeNil())
+// * Expect(x).Should(Equal(nil) // ===> Expect(x).Should(BeNil())
+
+const (
+ linterName = "ginkgo-linter"
+ wrongLengthWarningTemplate = linterName + ": wrong length assertion; consider using `%s` instead"
+ wrongNilWarningTemplate = linterName + ": wrong nil assertion; consider using `%s` instead"
+ wrongBoolWarningTemplate = linterName + ": wrong boolean assertion; consider using `%s` instead"
+ wrongErrWarningTemplate = linterName + ": wrong error assertion; consider using `%s` instead"
+ beEmpty = "BeEmpty"
+ beNil = "BeNil"
+ beTrue = "BeTrue"
+ beFalse = "BeFalse"
+ equal = "Equal"
+ not = "Not"
+ haveLen = "HaveLen"
+ succeed = "Succeed"
+ haveOccurred = "HaveOccurred"
+ expect = "Expect"
+ omega = "Ω"
+ expectWithOffset = "ExpectWithOffset"
+)
+
+// Analyzer is the interface to go_vet
+var Analyzer = NewAnalyzer()
+
+type ginkgoLinter struct {
+ suppress *types.Suppress
+}
+
+// NewAnalyzer returns an Analyzer - the package interface with nogo
+func NewAnalyzer() *analysis.Analyzer {
+ linter := ginkgoLinter{
+ suppress: &types.Suppress{
+ Len: false,
+ Nil: false,
+ Err: false,
+ },
+ }
+
+ a := &analysis.Analyzer{
+ Name: "ginkgolinter",
+ Doc: `enforces standards of using ginkgo and gomega
+currently, the linter searches for following:
+* wrong length assertions. We want to assert the item rather than its length.
+For example:
+ Expect(len(x)).Should(Equal(1))
+This should be replaced with:
+ Expect(x)).Should(HavelLen(1))
+
+* wrong nil assertions. We want to assert the item rather than a comparison result.
+For example:
+ Expect(x == nil).Should(BeTrue())
+This should be replaced with:
+ Expect(x).Should(BeNil())
+ `,
+ Run: linter.run,
+ RunDespiteErrors: true,
+ }
+
+ a.Flags.Init("ginkgolinter", flag.ExitOnError)
+ a.Flags.Var(&linter.suppress.Len, "suppress-len-assertion", "Suppress warning for wrong length assertions")
+ a.Flags.Var(&linter.suppress.Nil, "suppress-nil-assertion", "Suppress warning for wrong nil assertions")
+ a.Flags.Var(&linter.suppress.Err, "suppress-err-assertion", "Suppress warning for wrong error assertions")
+
+ return a
+}
+
+// main assertion function
+func (l *ginkgoLinter) run(pass *analysis.Pass) (interface{}, error) {
+ if l.suppress.AllTrue() {
+ return nil, nil
+ }
+
+ for _, file := range pass.Files {
+ fileSuppress := l.suppress.Clone()
+
+ cm := ast.NewCommentMap(pass.Fset, file, file.Comments)
+
+ fileSuppress.UpdateFromFile(cm)
+ if fileSuppress.AllTrue() {
+ continue
+ }
+
+ handler := gomegahandler.GetGomegaHandler(file)
+ if handler == nil { // no gomega import => no use in gomega in this file; nothing to do here
+ continue
+ }
+
+ ast.Inspect(file, func(n ast.Node) bool {
+
+ stmt, ok := n.(*ast.ExprStmt)
+ if !ok {
+ return true
+ }
+
+ exprSuppress := fileSuppress.Clone()
+
+ if comments, ok := cm[stmt]; ok {
+ exprSuppress.UpdateFromComment(comments)
+ }
+
+ // search for function calls
+ assertionExp, ok := stmt.X.(*ast.CallExpr)
+ if !ok {
+ return true
+ }
+
+ assertionFunc, ok := assertionExp.Fun.(*ast.SelectorExpr)
+ if !ok || !isAssertionFunc(assertionFunc.Sel.Name) {
+ return true
+ }
+
+ actualArg := getActualArg(assertionFunc, handler)
+ if actualArg == nil {
+ return true
+ }
+
+ return checkExpression(pass, exprSuppress, actualArg, assertionExp, handler)
+
+ })
+ }
+ return nil, nil
+}
+
+func checkExpression(pass *analysis.Pass, exprSuppress types.Suppress, actualArg ast.Expr, assertionExp *ast.CallExpr, handler gomegahandler.Handler) bool {
+ assertionExp = astcopy.CallExpr(assertionExp)
+ oldExpr := goFmt(pass.Fset, assertionExp)
+ if !bool(exprSuppress.Len) && isActualIsLenFunc(actualArg) {
+
+ return checkLengthMatcher(assertionExp, pass, handler, oldExpr)
+ } else {
+ if nilable, compOp := getNilableFromComparison(actualArg); nilable != nil {
+ if isExprError(pass, nilable) {
+ if exprSuppress.Err {
+ return true
+ }
+ } else if exprSuppress.Nil {
+ return true
+ }
+
+ return checkNilMatcher(assertionExp, pass, nilable, handler, compOp == token.NEQ, oldExpr)
+
+ } else if isExprError(pass, actualArg) {
+ return bool(exprSuppress.Err) || checkNilError(pass, assertionExp, handler, actualArg, oldExpr)
+
+ } else {
+ return simplifyEqual(pass, exprSuppress, assertionExp, handler, actualArg, oldExpr)
+ }
+ }
+}
+
+// Check if the "actual" argument is a call to the golang built-in len() function
+func isActualIsLenFunc(actualArg ast.Expr) bool {
+ lenArgExp, ok := actualArg.(*ast.CallExpr)
+ if !ok {
+ return false
+ }
+
+ lenFunc, ok := lenArgExp.Fun.(*ast.Ident)
+ return ok && lenFunc.Name == "len"
+}
+
+// Check if matcher function is in one of the patterns we want to avoid
+func checkLengthMatcher(exp *ast.CallExpr, pass *analysis.Pass, handler gomegahandler.Handler, oldExp string) bool {
+ matcher, ok := exp.Args[0].(*ast.CallExpr)
+ if !ok {
+ return true
+ }
+
+ matcherFuncName, ok := handler.GetActualFuncName(matcher)
+ if !ok {
+ return true
+ }
+
+ switch matcherFuncName {
+ case equal:
+ handleEqualMatcher(matcher, pass, exp, handler, oldExp)
+ return false
+
+ case "BeZero":
+ handleBeZero(pass, exp, handler, oldExp)
+ return false
+
+ case "BeNumerically":
+ return handleBeNumerically(matcher, pass, exp, handler, oldExp)
+
+ case not:
+ reverseAssertionFuncLogic(exp)
+ exp.Args[0] = exp.Args[0].(*ast.CallExpr).Args[0]
+ return checkLengthMatcher(exp, pass, handler, oldExp)
+
+ default:
+ return true
+ }
+}
+
+// Check if matcher function is in one of the patterns we want to avoid
+func checkNilMatcher(exp *ast.CallExpr, pass *analysis.Pass, nilable ast.Expr, handler gomegahandler.Handler, notEqual bool, oldExp string) bool {
+ matcher, ok := exp.Args[0].(*ast.CallExpr)
+ if !ok {
+ return true
+ }
+
+ matcherFuncName, ok := handler.GetActualFuncName(matcher)
+ if !ok {
+ return true
+ }
+
+ switch matcherFuncName {
+ case equal:
+ handleEqualNilMatcher(matcher, pass, exp, handler, nilable, notEqual, oldExp)
+
+ case beTrue:
+ handleNilBeBoolMatcher(pass, exp, handler, nilable, notEqual, oldExp)
+
+ case beFalse:
+ reverseAssertionFuncLogic(exp)
+ handleNilBeBoolMatcher(pass, exp, handler, nilable, notEqual, oldExp)
+
+ case not:
+ reverseAssertionFuncLogic(exp)
+ exp.Args[0] = exp.Args[0].(*ast.CallExpr).Args[0]
+ return checkNilMatcher(exp, pass, nilable, handler, notEqual, oldExp)
+
+ default:
+ return true
+ }
+ return false
+}
+
+func checkNilError(pass *analysis.Pass, assertionExp *ast.CallExpr, handler gomegahandler.Handler, actualArg ast.Expr, oldExpr string) bool {
+ if len(assertionExp.Args) == 0 {
+ return true
+ }
+
+ equalFuncExpr, ok := assertionExp.Args[0].(*ast.CallExpr)
+ if !ok {
+ return true
+ }
+
+ funcName, ok := handler.GetActualFuncName(equalFuncExpr)
+ if !ok {
+ return true
+ }
+
+ switch funcName {
+ case beNil: // no additional processing needed.
+ case equal:
+
+ if len(equalFuncExpr.Args) == 0 {
+ return true
+ }
+
+ nilable, ok := equalFuncExpr.Args[0].(*ast.Ident)
+ if !ok || nilable.Name != "nil" {
+ return true
+ }
+
+ case not:
+ reverseAssertionFuncLogic(assertionExp)
+ assertionExp.Args[0] = assertionExp.Args[0].(*ast.CallExpr).Args[0]
+ return checkNilError(pass, assertionExp, handler, actualArg, oldExpr)
+ default:
+ return true
+ }
+
+ var newFuncName string
+ if _, ok := actualArg.(*ast.CallExpr); ok {
+ newFuncName = succeed
+ } else {
+ reverseAssertionFuncLogic(assertionExp)
+ newFuncName = haveOccurred
+ }
+
+ handler.ReplaceFunction(equalFuncExpr, ast.NewIdent(newFuncName))
+ equalFuncExpr.Args = nil
+
+ report(pass, assertionExp, wrongErrWarningTemplate, oldExpr)
+ return false
+}
+
+// handle Equal(nil), Equal(true) and Equal(false)
+func simplifyEqual(pass *analysis.Pass, exprSuppress types.Suppress, assertionExp *ast.CallExpr, handler gomegahandler.Handler, actualArg ast.Expr, oldExpr string) bool {
+ if len(assertionExp.Args) == 0 {
+ return true
+ }
+
+ equalFuncExpr, ok := assertionExp.Args[0].(*ast.CallExpr)
+ if !ok {
+ return true
+ }
+
+ funcName, ok := handler.GetActualFuncName(equalFuncExpr)
+ if !ok {
+ return true
+ }
+
+ switch funcName {
+ case equal:
+ if len(equalFuncExpr.Args) == 0 {
+ return true
+ }
+
+ token, ok := equalFuncExpr.Args[0].(*ast.Ident)
+ if !ok {
+ return true
+ }
+
+ var replacement string
+ var template string
+ switch token.Name {
+ case "nil":
+ if exprSuppress.Nil {
+ return true
+ }
+ replacement = beNil
+ template = wrongNilWarningTemplate
+ case "true":
+ replacement = beTrue
+ template = wrongBoolWarningTemplate
+ case "false":
+ replacement = beFalse
+ template = wrongBoolWarningTemplate
+ default:
+ return true
+ }
+
+ handler.ReplaceFunction(equalFuncExpr, ast.NewIdent(replacement))
+ equalFuncExpr.Args = nil
+
+ report(pass, assertionExp, template, oldExpr)
+
+ return false
+
+ case not:
+ reverseAssertionFuncLogic(assertionExp)
+ assertionExp.Args[0] = assertionExp.Args[0].(*ast.CallExpr).Args[0]
+ return simplifyEqual(pass, exprSuppress, assertionExp, handler, actualArg, oldExpr)
+ default:
+ return true
+ }
+}
+
+// checks that the function is an assertion's actual function and return the "actual" parameter. If the function
+// is not assertion's actual function, return nil.
+func getActualArg(assertionFunc *ast.SelectorExpr, handler gomegahandler.Handler) ast.Expr {
+ actualExpr, ok := assertionFunc.X.(*ast.CallExpr)
+ if !ok {
+ return nil
+ }
+
+ funcName, ok := handler.GetActualFuncName(actualExpr)
+ if !ok {
+ return nil
+ }
+
+ switch funcName {
+ case expect, omega:
+ return actualExpr.Args[0]
+ case expectWithOffset:
+ return actualExpr.Args[1]
+ default:
+ return nil
+ }
+}
+
+// Replace the len function call by its parameter, to create a fix suggestion
+func replaceLenActualArg(actualExpr *ast.CallExpr, handler gomegahandler.Handler) {
+ name, ok := handler.GetActualFuncName(actualExpr)
+ if !ok {
+ return
+ }
+
+ switch name {
+ case expect, omega:
+ arg := actualExpr.Args[0]
+ if isActualIsLenFunc(arg) {
+ // replace the len function call by its parameter, to create a fix suggestion
+ actualExpr.Args[0] = arg.(*ast.CallExpr).Args[0]
+ }
+ case expectWithOffset:
+ arg := actualExpr.Args[1]
+ if isActualIsLenFunc(arg) {
+ // replace the len function call by its parameter, to create a fix suggestion
+ actualExpr.Args[1] = arg.(*ast.CallExpr).Args[0]
+ }
+ }
+}
+
+// Replace the nil comparison with the compared object, to create a fix suggestion
+func replaceNilActualArg(actualExpr *ast.CallExpr, handler gomegahandler.Handler, nilable ast.Expr) bool {
+ actualFuncName, ok := handler.GetActualFuncName(actualExpr)
+ if !ok {
+ return false
+ }
+
+ switch actualFuncName {
+ case expect, omega:
+ actualExpr.Args[0] = nilable
+ return true
+
+ case expectWithOffset:
+ actualExpr.Args[1] = nilable
+ return true
+
+ default:
+ return false
+ }
+}
+
+// For the BeNumerically matcher, we want to avoid the assertion of length to be > 0 or >= 1, or just == number
+func handleBeNumerically(matcher *ast.CallExpr, pass *analysis.Pass, exp *ast.CallExpr, handler gomegahandler.Handler, oldExp string) bool {
+ opExp, ok1 := matcher.Args[0].(*ast.BasicLit)
+ valExp, ok2 := matcher.Args[1].(*ast.BasicLit)
+
+ if ok1 && ok2 {
+ op := opExp.Value
+ val := valExp.Value
+
+ if (op == `">"` && val == "0") || (op == `">="` && val == "1") {
+ reverseAssertionFuncLogic(exp)
+ handler.ReplaceFunction(exp.Args[0].(*ast.CallExpr), ast.NewIdent(beEmpty))
+ exp.Args[0].(*ast.CallExpr).Args = nil
+ reportLengthAssertion(pass, exp, handler, oldExp)
+ return false
+ } else if op == `"=="` {
+ chooseNumericMatcher(exp, handler, valExp)
+ reportLengthAssertion(pass, exp, handler, oldExp)
+
+ return false
+ } else if op == `"!="` {
+ reverseAssertionFuncLogic(exp)
+ chooseNumericMatcher(exp, handler, valExp)
+ reportLengthAssertion(pass, exp, handler, oldExp)
+
+ return false
+ }
+ }
+ return true
+}
+
+func chooseNumericMatcher(exp *ast.CallExpr, handler gomegahandler.Handler, valExp *ast.BasicLit) {
+ caller := exp.Args[0].(*ast.CallExpr)
+ if valExp.Value == "0" {
+ handler.ReplaceFunction(caller, ast.NewIdent(beEmpty))
+ exp.Args[0].(*ast.CallExpr).Args = nil
+ } else {
+ handler.ReplaceFunction(caller, ast.NewIdent(haveLen))
+ exp.Args[0].(*ast.CallExpr).Args = []ast.Expr{valExp}
+ }
+}
+
+func reverseAssertionFuncLogic(exp *ast.CallExpr) {
+ assertionFunc := exp.Fun.(*ast.SelectorExpr).Sel
+ assertionFunc.Name = reverseassertion.ChangeAssertionLogic(assertionFunc.Name)
+}
+
+func handleEqualMatcher(matcher *ast.CallExpr, pass *analysis.Pass, exp *ast.CallExpr, handler gomegahandler.Handler, oldExp string) {
+ equalTo, ok := matcher.Args[0].(*ast.BasicLit)
+ if ok {
+ chooseNumericMatcher(exp, handler, equalTo)
+ } else {
+ handler.ReplaceFunction(exp.Args[0].(*ast.CallExpr), ast.NewIdent(haveLen))
+ exp.Args[0].(*ast.CallExpr).Args = []ast.Expr{matcher.Args[0]}
+ }
+ reportLengthAssertion(pass, exp, handler, oldExp)
+}
+
+func handleBeZero(pass *analysis.Pass, exp *ast.CallExpr, handler gomegahandler.Handler, oldExp string) {
+ exp.Args[0].(*ast.CallExpr).Args = nil
+ handler.ReplaceFunction(exp.Args[0].(*ast.CallExpr), ast.NewIdent(beEmpty))
+ reportLengthAssertion(pass, exp, handler, oldExp)
+}
+
+func handleEqualNilMatcher(matcher *ast.CallExpr, pass *analysis.Pass, exp *ast.CallExpr, handler gomegahandler.Handler, nilable ast.Expr, notEqual bool, oldExp string) {
+ equalTo, ok := matcher.Args[0].(*ast.Ident)
+ if !ok {
+ return
+ }
+
+ if equalTo.Name == "false" {
+ reverseAssertionFuncLogic(exp)
+ } else if equalTo.Name != "true" {
+ return
+ }
+
+ newFuncName, isItError := handleNilComparisonErr(pass, exp, nilable)
+
+ handler.ReplaceFunction(exp.Args[0].(*ast.CallExpr), ast.NewIdent(newFuncName))
+ exp.Args[0].(*ast.CallExpr).Args = nil
+
+ reportNilAssertion(pass, exp, handler, nilable, notEqual, oldExp, isItError)
+}
+
+func handleNilBeBoolMatcher(pass *analysis.Pass, exp *ast.CallExpr, handler gomegahandler.Handler, nilable ast.Expr, notEqual bool, oldExp string) {
+ newFuncName, isItError := handleNilComparisonErr(pass, exp, nilable)
+ handler.ReplaceFunction(exp.Args[0].(*ast.CallExpr), ast.NewIdent(newFuncName))
+ exp.Args[0].(*ast.CallExpr).Args = nil
+
+ reportNilAssertion(pass, exp, handler, nilable, notEqual, oldExp, isItError)
+}
+
+func handleNilComparisonErr(pass *analysis.Pass, exp *ast.CallExpr, nilable ast.Expr) (string, bool) {
+ newFuncName := beNil
+ isItError := isExprError(pass, nilable)
+ if isItError {
+ if _, ok := nilable.(*ast.CallExpr); ok {
+ newFuncName = succeed
+ } else {
+ reverseAssertionFuncLogic(exp)
+ newFuncName = haveOccurred
+ }
+ }
+
+ return newFuncName, isItError
+}
+func isAssertionFunc(name string) bool {
+ switch name {
+ case "To", "ToNot", "NotTo", "Should", "ShouldNot":
+ return true
+ }
+ return false
+}
+
+func reportLengthAssertion(pass *analysis.Pass, expr *ast.CallExpr, handler gomegahandler.Handler, oldExpr string) {
+ replaceLenActualArg(expr.Fun.(*ast.SelectorExpr).X.(*ast.CallExpr), handler)
+
+ report(pass, expr, wrongLengthWarningTemplate, oldExpr)
+}
+
+func reportNilAssertion(pass *analysis.Pass, expr *ast.CallExpr, handler gomegahandler.Handler, nilable ast.Expr, notEqual bool, oldExpr string, isItError bool) {
+ changed := replaceNilActualArg(expr.Fun.(*ast.SelectorExpr).X.(*ast.CallExpr), handler, nilable)
+ if !changed {
+ return
+ }
+
+ if notEqual {
+ reverseAssertionFuncLogic(expr)
+ }
+ template := wrongNilWarningTemplate
+ if isItError {
+ template = wrongErrWarningTemplate
+ }
+
+ report(pass, expr, template, oldExpr)
+}
+
+func report(pass *analysis.Pass, expr *ast.CallExpr, messageTemplate, oldExpr string) {
+ newExp := goFmt(pass.Fset, expr)
+ pass.Report(analysis.Diagnostic{
+ Pos: expr.Pos(),
+ Message: fmt.Sprintf(messageTemplate, newExp),
+ SuggestedFixes: []analysis.SuggestedFix{
+ {
+ Message: fmt.Sprintf("should replace %s with %s", oldExpr, newExp),
+ TextEdits: []analysis.TextEdit{
+ {
+ Pos: expr.Pos(),
+ End: expr.End(),
+ NewText: []byte(newExp),
+ },
+ },
+ },
+ },
+ })
+}
+
+func getNilableFromComparison(actualArg ast.Expr) (ast.Expr, token.Token) {
+ bin, ok := actualArg.(*ast.BinaryExpr)
+ if !ok {
+ return nil, token.ILLEGAL
+ }
+
+ if bin.Op == token.EQL || bin.Op == token.NEQ {
+ if isNil(bin.Y) {
+ return bin.X, bin.Op
+ } else if isNil(bin.X) {
+ return bin.Y, bin.Op
+ }
+ }
+
+ return nil, token.ILLEGAL
+}
+
+func isNil(expr ast.Expr) bool {
+ nilObject, ok := expr.(*ast.Ident)
+ return ok && nilObject.Name == "nil" && nilObject.Obj == nil
+}
+
+func goFmt(fset *token.FileSet, x ast.Expr) string {
+ var b bytes.Buffer
+ _ = printer.Fprint(&b, fset, x)
+ return b.String()
+}
+
+var errorType *gotypes.Interface
+
+func init() {
+ errorType = gotypes.Universe.Lookup("error").Type().Underlying().(*gotypes.Interface)
+}
+
+func isError(t gotypes.Type) bool {
+ return gotypes.Implements(t, errorType)
+}
+
+func isExprError(pass *analysis.Pass, expr ast.Expr) bool {
+ actualArgType := pass.TypesInfo.TypeOf(expr)
+ switch t := actualArgType.(type) {
+ case *gotypes.Named:
+ if isError(actualArgType) {
+ return true
+ }
+ case *gotypes.Tuple:
+ if t.Len() > 0 {
+ switch t0 := t.At(0).Type().(type) {
+ case *gotypes.Named, *gotypes.Pointer:
+ if isError(t0) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go b/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go
new file mode 100644
index 000000000..d57ba2913
--- /dev/null
+++ b/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go
@@ -0,0 +1,167 @@
+package gomegahandler
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// Handler provide different handling, depend on the way gomega was imported, whether
+// in imported with "." name, custom name or without any name.
+type Handler interface {
+ // GetActualFuncName returns the name of the gomega function, e.g. `Expect`
+ GetActualFuncName(*ast.CallExpr) (string, bool)
+ // ReplaceFunction replaces the function with another one, for fix suggestions
+ ReplaceFunction(*ast.CallExpr, *ast.Ident)
+
+ getDefFuncName(expr *ast.CallExpr) string
+
+ getFieldType(field *ast.Field) string
+}
+
+// GetGomegaHandler returns a gomegar handler according to the way gomega was imported in the specific file
+func GetGomegaHandler(file *ast.File) Handler {
+ for _, imp := range file.Imports {
+ if imp.Path.Value != `"github.com/onsi/gomega"` {
+ continue
+ }
+
+ switch name := imp.Name.String(); {
+ case name == ".":
+ return dotHandler{}
+ case name == "<nil>": // import with no local name
+ return nameHandler("gomega")
+ default:
+ return nameHandler(name)
+ }
+ }
+
+ return nil // no gomega import; this file does not use gomega
+}
+
+// dotHandler is used when importing gomega with dot; i.e.
+// import . "github.com/onsi/gomega"
+type dotHandler struct{}
+
+// GetActualFuncName returns the name of the gomega function, e.g. `Expect`
+func (h dotHandler) GetActualFuncName(expr *ast.CallExpr) (string, bool) {
+ switch actualFunc := expr.Fun.(type) {
+ case *ast.Ident:
+ return actualFunc.Name, true
+ case *ast.SelectorExpr:
+ if isGomegaVar(actualFunc.X, h) {
+ return actualFunc.Sel.Name, true
+ }
+ }
+ return "", false
+}
+
+// ReplaceFunction replaces the function with another one, for fix suggestions
+func (dotHandler) ReplaceFunction(caller *ast.CallExpr, newExpr *ast.Ident) {
+ caller.Fun = newExpr
+}
+
+func (dotHandler) getDefFuncName(expr *ast.CallExpr) string {
+ if f, ok := expr.Fun.(*ast.Ident); ok {
+ return f.Name
+ }
+ return ""
+}
+
+func (dotHandler) getFieldType(field *ast.Field) string {
+ switch t := field.Type.(type) {
+ case *ast.Ident:
+ return t.Name
+ case *ast.StarExpr:
+ if name, ok := t.X.(*ast.Ident); ok {
+ return name.Name
+ }
+ }
+ return ""
+}
+
+// nameHandler is used when importing gomega without name; i.e.
+// import "github.com/onsi/gomega"
+//
+// or with a custom name; e.g.
+// import customname "github.com/onsi/gomega"
+type nameHandler string
+
+// GetActualFuncName returns the name of the gomega function, e.g. `Expect`
+func (g nameHandler) GetActualFuncName(expr *ast.CallExpr) (string, bool) {
+ selector, ok := expr.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return "", false
+ }
+
+ x, ok := selector.X.(*ast.Ident)
+ if !ok {
+ return "", false
+ }
+
+ if x.Name != string(g) {
+ if !isGomegaVar(x, g) {
+ return "", false
+ }
+ }
+
+ return selector.Sel.Name, true
+}
+
+// ReplaceFunction replaces the function with another one, for fix suggestions
+func (nameHandler) ReplaceFunction(caller *ast.CallExpr, newExpr *ast.Ident) {
+ caller.Fun.(*ast.SelectorExpr).Sel = newExpr
+}
+
+func (g nameHandler) getDefFuncName(expr *ast.CallExpr) string {
+ if sel, ok := expr.Fun.(*ast.SelectorExpr); ok {
+ if f, ok := sel.X.(*ast.Ident); ok && f.Name == string(g) {
+ return sel.Sel.Name
+ }
+ }
+ return ""
+}
+
+func (g nameHandler) getFieldType(field *ast.Field) string {
+ switch t := field.Type.(type) {
+ case *ast.SelectorExpr:
+ if id, ok := t.X.(*ast.Ident); ok {
+ if id.Name == string(g) {
+ return t.Sel.Name
+ }
+ }
+ case *ast.StarExpr:
+ if sel, ok := t.X.(*ast.SelectorExpr); ok {
+ if x, ok := sel.X.(*ast.Ident); ok && x.Name == string(g) {
+ return sel.Sel.Name
+ }
+ }
+
+ }
+ return ""
+}
+
+func isGomegaVar(x ast.Expr, handler Handler) bool {
+ if i, ok := x.(*ast.Ident); ok {
+ if i.Obj != nil && i.Obj.Kind == ast.Var {
+ switch decl := i.Obj.Decl.(type) {
+ case *ast.AssignStmt:
+ if decl.Tok == token.DEFINE {
+ if defFunc, ok := decl.Rhs[0].(*ast.CallExpr); ok {
+ fName := handler.getDefFuncName(defFunc)
+ switch fName {
+ case "NewGomega", "NewWithT", "NewGomegaWithT":
+ return true
+ }
+ }
+ }
+ case *ast.Field:
+ name := handler.getFieldType(decl)
+ switch name {
+ case "Gomega", "WithT", "GomegaWithT":
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/nunnatsa/ginkgolinter/reverseassertion/reverse_assertion.go b/vendor/github.com/nunnatsa/ginkgolinter/reverseassertion/reverse_assertion.go
new file mode 100644
index 000000000..42c914e50
--- /dev/null
+++ b/vendor/github.com/nunnatsa/ginkgolinter/reverseassertion/reverse_assertion.go
@@ -0,0 +1,17 @@
+package reverseassertion
+
+var reverseLogicAssertions = map[string]string{
+ "To": "ToNot",
+ "ToNot": "To",
+ "NotTo": "To",
+ "Should": "ShouldNot",
+ "ShouldNot": "Should",
+}
+
+// ChangeAssertionLogic get gomega assertion function name, and returns the reverse logic function name
+func ChangeAssertionLogic(funcName string) string {
+ if revFunc, ok := reverseLogicAssertions[funcName]; ok {
+ return revFunc
+ }
+ return funcName
+}
diff --git a/vendor/github.com/nunnatsa/ginkgolinter/types/boolean.go b/vendor/github.com/nunnatsa/ginkgolinter/types/boolean.go
new file mode 100644
index 000000000..be510c4e9
--- /dev/null
+++ b/vendor/github.com/nunnatsa/ginkgolinter/types/boolean.go
@@ -0,0 +1,32 @@
+package types
+
+import (
+ "errors"
+ "strings"
+)
+
+// Boolean is a bool, implementing the flag.Value interface, to be used as a flag var.
+type Boolean bool
+
+func (b *Boolean) Set(value string) error {
+ if b == nil {
+ return errors.New("trying to set nil parameter")
+ }
+ switch strings.ToLower(value) {
+ case "true":
+ *b = true
+ case "false":
+ *b = false
+ default:
+ return errors.New(value + " is not a Boolean value")
+
+ }
+ return nil
+}
+
+func (b Boolean) String() string {
+ if b {
+ return "true"
+ }
+ return "false"
+}
diff --git a/vendor/github.com/nunnatsa/ginkgolinter/types/suppress.go b/vendor/github.com/nunnatsa/ginkgolinter/types/suppress.go
new file mode 100644
index 000000000..a703eb2d3
--- /dev/null
+++ b/vendor/github.com/nunnatsa/ginkgolinter/types/suppress.go
@@ -0,0 +1,67 @@
+package types
+
+import (
+ "strings"
+
+ "go/ast"
+)
+
+const (
+ suppressPrefix = "ginkgo-linter:"
+ suppressLengthAssertionWarning = suppressPrefix + "ignore-len-assert-warning"
+ suppressNilAssertionWarning = suppressPrefix + "ignore-nil-assert-warning"
+ suppressErrAssertionWarning = suppressPrefix + "ignore-err-assert-warning"
+)
+
+type Suppress struct {
+ Len Boolean
+ Nil Boolean
+ Err Boolean
+}
+
+func (s Suppress) AllTrue() bool {
+ return bool(s.Len && s.Nil && s.Err)
+}
+
+func (s Suppress) Clone() Suppress {
+ return Suppress{
+ Len: s.Len,
+ Nil: s.Nil,
+ Err: s.Err,
+ }
+}
+
+func (s *Suppress) UpdateFromComment(commentGroup []*ast.CommentGroup) {
+ for _, cmntList := range commentGroup {
+ if s.AllTrue() {
+ break
+ }
+
+ for _, cmnt := range cmntList.List {
+ commentLines := strings.Split(cmnt.Text, "\n")
+ for _, comment := range commentLines {
+ comment = strings.TrimPrefix(comment, "//")
+ comment = strings.TrimPrefix(comment, "/*")
+ comment = strings.TrimSuffix(comment, "*/")
+ comment = strings.TrimSpace(comment)
+
+ s.Len = s.Len || (comment == suppressLengthAssertionWarning)
+ s.Nil = s.Nil || (comment == suppressNilAssertionWarning)
+ s.Err = s.Err || (comment == suppressErrAssertionWarning)
+ }
+ }
+ }
+}
+
+func (s *Suppress) UpdateFromFile(cm ast.CommentMap) {
+
+ for key, commentGroup := range cm {
+ if s.AllTrue() {
+ break
+ }
+
+ if _, ok := key.(*ast.GenDecl); ok {
+ s.UpdateFromComment(commentGroup)
+ }
+ }
+}