aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Antonboom
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2024-09-10 12:16:33 +0200
committerTaras Madan <tarasmadan@google.com>2024-09-10 14:05:26 +0000
commitc97c816133b42257d0bcf1ee4bd178bb2a7a2b9e (patch)
tree0bcbc2e540bbf8f62f6c17887cdd53b8c2cee637 /vendor/github.com/Antonboom
parent54e657429ab892ad06c90cd7c1a4eb33ba93a3dc (diff)
vendor: update
Diffstat (limited to 'vendor/github.com/Antonboom')
-rw-r--r--vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go136
-rw-r--r--vendor/github.com/Antonboom/nilnil/pkg/analyzer/config.go28
-rw-r--r--vendor/github.com/Antonboom/testifylint/analyzer/analyzer.go2
-rw-r--r--vendor/github.com/Antonboom/testifylint/analyzer/checkers_factory.go7
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/bool_compare.go92
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/call_meta.go35
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go4
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/compares.go45
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go69
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/error_is_as.go24
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/error_nil.go21
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go46
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go26
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go187
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/go_require.go84
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers.go43
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go113
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_bool.go33
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_comparison.go68
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_context.go126
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go (renamed from vendor/github.com/Antonboom/testifylint/internal/checkers/diagnostic.go)20
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_error.go42
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go26
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_http.go35
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go48
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_len.go55
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_naming.go26
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_nil.go18
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_suite.go40
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_testing.go36
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/len.go33
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go175
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/nil_compare.go13
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/printf/LICENSE27
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/printf/doc.go6
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go559
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/require_error.go106
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/suite_broken_parallel.go89
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/suite_dont_use_pkg.go20
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/suite_extra_assert_call.go6
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/suite_subtest_run.go60
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/suite_thelper.go69
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/checkers/useless_assert.go27
-rw-r--r--vendor/github.com/Antonboom/testifylint/internal/config/config.go53
44 files changed, 2159 insertions, 619 deletions
diff --git a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go
index e980db546..5646ee909 100644
--- a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go
+++ b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go
@@ -2,6 +2,9 @@ package analyzer
import (
"go/ast"
+ "go/token"
+ "go/types"
+ "strconv"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
@@ -40,29 +43,15 @@ func newNilNil() *nilNil {
}
}
-var (
- types = []ast.Node{(*ast.TypeSpec)(nil)}
-
- funcAndReturns = []ast.Node{
- (*ast.FuncDecl)(nil),
- (*ast.FuncLit)(nil),
- (*ast.ReturnStmt)(nil),
- }
-)
-
-type typeSpecByName map[string]typer
+var funcAndReturns = []ast.Node{
+ (*ast.FuncDecl)(nil),
+ (*ast.FuncLit)(nil),
+ (*ast.ReturnStmt)(nil),
+}
func (n *nilNil) run(pass *analysis.Pass) (interface{}, error) {
insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
- typeSpecs := typeSpecByName{
- "any": newTyper(new(ast.InterfaceType)),
- }
- insp.Preorder(types, func(node ast.Node) {
- t := node.(*ast.TypeSpec)
- typeSpecs[t.Name.Name] = newTyper(t.Type)
- })
-
var fs funcTypeStack
insp.Nodes(funcAndReturns, func(node ast.Node, push bool) (proceed bool) {
switch v := node.(type) {
@@ -87,13 +76,32 @@ func (n *nilNil) run(pass *analysis.Pass) (interface{}, error) {
return false
}
- fRes1, fRes2 := ft.Results.List[0], ft.Results.List[1]
- if !(n.isDangerNilField(fRes1, typeSpecs) && n.isErrorField(fRes2)) {
+ fRes1Type := pass.TypesInfo.TypeOf(ft.Results.List[0].Type)
+ if fRes1Type == nil {
return false
}
- rRes1, rRes2 := v.Results[0], v.Results[1]
- if isNil(rRes1) && isNil(rRes2) {
+ fRes2Type := pass.TypesInfo.TypeOf(ft.Results.List[1].Type)
+ if fRes2Type == nil {
+ return false
+ }
+
+ ok, zv := n.isDangerNilType(fRes1Type)
+ if !(ok && isErrorType(fRes2Type)) {
+ return false
+ }
+
+ retVal, retErr := v.Results[0], v.Results[1]
+
+ var needWarn bool
+ switch zv {
+ case zeroValueNil:
+ needWarn = isNil(pass, retVal) && isNil(pass, retErr)
+ case zeroValueZero:
+ needWarn = isZero(retVal) && isNil(pass, retErr)
+ }
+
+ if needWarn {
pass.Reportf(v.Pos(), reportMsg)
}
}
@@ -104,55 +112,73 @@ func (n *nilNil) run(pass *analysis.Pass) (interface{}, error) {
return nil, nil //nolint:nilnil
}
-func (n *nilNil) isDangerNilField(f *ast.Field, typeSpecs typeSpecByName) bool {
- return n.isDangerNilType(f.Type, typeSpecs)
-}
+type zeroValue int
-func (n *nilNil) isDangerNilType(t ast.Expr, typeSpecs typeSpecByName) bool {
+const (
+ zeroValueNil = iota + 1
+ zeroValueZero
+)
+
+func (n *nilNil) isDangerNilType(t types.Type) (bool, zeroValue) {
switch v := t.(type) {
- case *ast.StarExpr:
- return n.checkedTypes.Contains(ptrType)
+ case *types.Pointer:
+ return n.checkedTypes.Contains(ptrType), zeroValueNil
- case *ast.FuncType:
- return n.checkedTypes.Contains(funcType)
+ case *types.Signature:
+ return n.checkedTypes.Contains(funcType), zeroValueNil
- case *ast.InterfaceType:
- return n.checkedTypes.Contains(ifaceType)
+ case *types.Interface:
+ return n.checkedTypes.Contains(ifaceType), zeroValueNil
- case *ast.MapType:
- return n.checkedTypes.Contains(mapType)
+ case *types.Map:
+ return n.checkedTypes.Contains(mapType), zeroValueNil
- case *ast.ChanType:
- return n.checkedTypes.Contains(chanType)
+ case *types.Chan:
+ return n.checkedTypes.Contains(chanType), zeroValueNil
- case *ast.Ident:
- if t, ok := typeSpecs[v.Name]; ok {
- return n.isDangerNilType(t.Type(), typeSpecs)
+ case *types.Basic:
+ if v.Kind() == types.Uintptr {
+ return n.checkedTypes.Contains(uintptrType), zeroValueZero
+ }
+ if v.Kind() == types.UnsafePointer {
+ return n.checkedTypes.Contains(unsafeptrType), zeroValueNil
}
+
+ case *types.Named:
+ return n.isDangerNilType(v.Underlying())
}
- return false
+ return false, 0
}
-func (n *nilNil) isErrorField(f *ast.Field) bool {
- return isIdent(f.Type, "error")
-}
+var errorIface = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
-func isNil(e ast.Expr) bool {
- return isIdent(e, "nil")
+func isErrorType(t types.Type) bool {
+ _, ok := t.Underlying().(*types.Interface)
+ return ok && types.Implements(t, errorIface)
}
-func isIdent(n ast.Node, name string) bool {
- i, ok := n.(*ast.Ident)
+func isNil(pass *analysis.Pass, e ast.Expr) bool {
+ i, ok := e.(*ast.Ident)
if !ok {
return false
}
- return i.Name == name
-}
-type typer interface {
- Type() ast.Expr
+ _, ok = pass.TypesInfo.ObjectOf(i).(*types.Nil)
+ return ok
}
-func newTyper(t ast.Expr) typer { return typerImpl{t: t} } //
-type typerImpl struct{ t ast.Expr } //
-func (ti typerImpl) Type() ast.Expr { return ti.t }
+func isZero(e ast.Expr) bool {
+ bl, ok := e.(*ast.BasicLit)
+ if !ok {
+ return false
+ }
+ if bl.Kind != token.INT {
+ return false
+ }
+
+ v, err := strconv.ParseInt(bl.Value, 0, 64)
+ if err != nil {
+ return false
+ }
+ return v == 0
+}
diff --git a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/config.go b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/config.go
index 520b813a5..c9b8e3eed 100644
--- a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/config.go
+++ b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/config.go
@@ -8,11 +8,13 @@ import (
func newDefaultCheckedTypes() checkedTypes {
return checkedTypes{
- ptrType: struct{}{},
- funcType: struct{}{},
- ifaceType: struct{}{},
- mapType: struct{}{},
- chanType: struct{}{},
+ ptrType: {},
+ funcType: {},
+ ifaceType: {},
+ mapType: {},
+ chanType: {},
+ uintptrType: {},
+ unsafeptrType: {},
}
}
@@ -25,15 +27,15 @@ func (t typeName) S() string {
}
const (
- ptrType typeName = "ptr"
- funcType typeName = "func"
- ifaceType typeName = "iface"
- mapType typeName = "map"
- chanType typeName = "chan"
+ ptrType typeName = "ptr"
+ funcType typeName = "func"
+ ifaceType typeName = "iface"
+ mapType typeName = "map"
+ chanType typeName = "chan"
+ uintptrType typeName = "uintptr"
+ unsafeptrType typeName = "unsafeptr"
)
-var knownTypes = []typeName{ptrType, funcType, ifaceType, mapType, chanType}
-
type checkedTypes map[typeName]struct{}
func (c checkedTypes) Contains(t typeName) bool {
@@ -60,7 +62,7 @@ func (c checkedTypes) Set(s string) error {
c.disableAll()
for _, t := range types {
switch tt := typeName(t); tt {
- case ptrType, funcType, ifaceType, mapType, chanType:
+ case ptrType, funcType, ifaceType, mapType, chanType, uintptrType, unsafeptrType:
c[tt] = struct{}{}
default:
return fmt.Errorf("unknown checked type name %q (see help)", t)
diff --git a/vendor/github.com/Antonboom/testifylint/analyzer/analyzer.go b/vendor/github.com/Antonboom/testifylint/analyzer/analyzer.go
index 84d7e815d..a9e41b0a8 100644
--- a/vendor/github.com/Antonboom/testifylint/analyzer/analyzer.go
+++ b/vendor/github.com/Antonboom/testifylint/analyzer/analyzer.go
@@ -19,7 +19,7 @@ const (
url = "https://github.com/antonboom/" + name
)
-// New returns new instance of testifylint analyzer.
+// New returns a new instance of testifylint analyzer.
func New() *analysis.Analyzer {
cfg := config.NewDefault()
diff --git a/vendor/github.com/Antonboom/testifylint/analyzer/checkers_factory.go b/vendor/github.com/Antonboom/testifylint/analyzer/checkers_factory.go
index 77573e395..df04dfdc5 100644
--- a/vendor/github.com/Antonboom/testifylint/analyzer/checkers_factory.go
+++ b/vendor/github.com/Antonboom/testifylint/analyzer/checkers_factory.go
@@ -55,6 +55,13 @@ func newCheckers(cfg config.Config) ([]checkers.RegularChecker, []checkers.Advan
case *checkers.ExpectedActual:
c.SetExpVarPattern(cfg.ExpectedActual.ExpVarPattern.Regexp)
+ case *checkers.Formatter:
+ c.SetCheckFormatString(cfg.Formatter.CheckFormatString)
+ c.SetRequireFFuncs(cfg.Formatter.RequireFFuncs)
+
+ case *checkers.GoRequire:
+ c.SetIgnoreHTTPHandlers(cfg.GoRequire.IgnoreHTTPHandlers)
+
case *checkers.RequireError:
c.SetFnPattern(cfg.RequireError.FnPattern.Regexp)
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/bool_compare.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/bool_compare.go
index 43907123b..d125c43f9 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/bool_compare.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/bool_compare.go
@@ -3,7 +3,6 @@ package checkers
import (
"go/ast"
"go/token"
- "go/types"
"golang.org/x/tools/go/analysis"
@@ -204,68 +203,6 @@ func (checker BoolCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.
return nil
}
-func isEmptyInterface(pass *analysis.Pass, expr ast.Expr) bool {
- t, ok := pass.TypesInfo.Types[expr]
- if !ok {
- return false
- }
-
- iface, ok := t.Type.Underlying().(*types.Interface)
- return ok && iface.NumMethods() == 0
-}
-
-func isBuiltinBool(pass *analysis.Pass, e ast.Expr) bool {
- basicType, ok := pass.TypesInfo.TypeOf(e).(*types.Basic)
- return ok && basicType.Kind() == types.Bool
-}
-
-func isBoolOverride(pass *analysis.Pass, e ast.Expr) bool {
- namedType, ok := pass.TypesInfo.TypeOf(e).(*types.Named)
- return ok && namedType.Obj().Name() == "bool"
-}
-
-var (
- falseObj = types.Universe.Lookup("false")
- trueObj = types.Universe.Lookup("true")
-)
-
-func isUntypedTrue(pass *analysis.Pass, e ast.Expr) bool {
- return analysisutil.IsObj(pass.TypesInfo, e, trueObj)
-}
-
-func isUntypedFalse(pass *analysis.Pass, e ast.Expr) bool {
- return analysisutil.IsObj(pass.TypesInfo, e, falseObj)
-}
-
-func isComparisonWithTrue(pass *analysis.Pass, e ast.Expr, op token.Token) (ast.Expr, bool) {
- return isComparisonWith(pass, e, isUntypedTrue, op)
-}
-
-func isComparisonWithFalse(pass *analysis.Pass, e ast.Expr, op token.Token) (ast.Expr, bool) {
- return isComparisonWith(pass, e, isUntypedFalse, op)
-}
-
-type predicate func(pass *analysis.Pass, e ast.Expr) bool
-
-func isComparisonWith(pass *analysis.Pass, e ast.Expr, predicate predicate, op token.Token) (ast.Expr, bool) {
- be, ok := e.(*ast.BinaryExpr)
- if !ok {
- return nil, false
- }
- if be.Op != op {
- return nil, false
- }
-
- t1, t2 := predicate(pass, be.X), predicate(pass, be.Y)
- if xor(t1, t2) {
- if t1 {
- return be.Y, true
- }
- return be.X, true
- }
- return nil, false
-}
-
func isNegation(e ast.Expr) (ast.Expr, bool) {
ue, ok := e.(*ast.UnaryExpr)
if !ok {
@@ -273,32 +210,3 @@ func isNegation(e ast.Expr) (ast.Expr, bool) {
}
return ue.X, ue.Op == token.NOT
}
-
-func xor(a, b bool) bool {
- return a != b
-}
-
-// anyVal returns the first value[i] for which bools[i] is true.
-func anyVal[T any](bools []bool, vals ...T) (T, bool) {
- if len(bools) != len(vals) {
- panic("inconsistent usage of valOr") //nolint:forbidigo // Does not depend on the code being analyzed.
- }
-
- for i, b := range bools {
- if b {
- return vals[i], true
- }
- }
-
- var _default T
- return _default, false
-}
-
-func anyCondSatisfaction(pass *analysis.Pass, p predicate, vals ...ast.Expr) bool {
- for _, v := range vals {
- if p(pass, v) {
- return true
- }
- }
- return false
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/call_meta.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/call_meta.go
index 44eed49a6..96b5b19b0 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/call_meta.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/call_meta.go
@@ -6,6 +6,7 @@ import (
"strings"
"golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/types/typeutil"
"github.com/Antonboom/testifylint/internal/analysisutil"
"github.com/Antonboom/testifylint/internal/testify"
@@ -15,6 +16,8 @@ import (
//
// assert.Equal(t, 42, result, "helpful comment")
type CallMeta struct {
+ // Call stores the original AST call expression.
+ Call *ast.CallExpr
// Range contains start and end position of assertion call.
analysis.Range
// IsPkg true if this is package (not object) call.
@@ -49,6 +52,8 @@ type FnMeta struct {
NameFTrimmed string
// IsFmt is true if function is formatted, e.g. "Equalf".
IsFmt bool
+ // Signature represents assertion signature.
+ Signature *types.Signature
}
// NewCallMeta returns meta information about testify assertion call.
@@ -66,16 +71,16 @@ func NewCallMeta(pass *analysis.Pass, ce *ast.CallExpr) *CallMeta {
// s.Assert().Equal -> method of *assert.Assertions -> package assert ("vendor/github.com/stretchr/testify/assert")
// s.Equal -> method of *assert.Assertions -> package assert ("vendor/github.com/stretchr/testify/assert")
// reqObj.Falsef -> method of *require.Assertions -> package require ("vendor/github.com/stretchr/testify/require")
- if sel, ok := pass.TypesInfo.Selections[se]; ok {
+ if sel, isSel := pass.TypesInfo.Selections[se]; isSel {
return sel.Obj().Pkg(), false
}
// Examples:
// assert.False -> assert -> package assert ("vendor/github.com/stretchr/testify/assert")
// require.NotEqualf -> require -> package require ("vendor/github.com/stretchr/testify/require")
- if id, ok := se.X.(*ast.Ident); ok {
+ if id, isIdent := se.X.(*ast.Ident); isIdent {
if selObj := pass.TypesInfo.ObjectOf(id); selObj != nil {
- if pkg, ok := selObj.(*types.PkgName); ok {
+ if pkg, isPkgName := selObj.(*types.PkgName); isPkgName {
return pkg.Imported(), true
}
}
@@ -92,7 +97,13 @@ func NewCallMeta(pass *analysis.Pass, ce *ast.CallExpr) *CallMeta {
return nil
}
+ funcObj, ok := typeutil.Callee(pass.TypesInfo, ce).(*types.Func)
+ if !ok {
+ return nil
+ }
+
return &CallMeta{
+ Call: ce,
Range: ce,
IsPkg: isPkgCall,
IsAssert: isAssert,
@@ -103,6 +114,7 @@ func NewCallMeta(pass *analysis.Pass, ce *ast.CallExpr) *CallMeta {
Name: fnName,
NameFTrimmed: strings.TrimSuffix(fnName, "f"),
IsFmt: strings.HasSuffix(fnName, "f"),
+ Signature: funcObj.Type().(*types.Signature), // NOTE(a.telyshev): Func's Type() is always a *Signature.
},
Args: trimTArg(pass, ce.Args),
ArgsRaw: ce.Args,
@@ -114,23 +126,8 @@ func trimTArg(pass *analysis.Pass, args []ast.Expr) []ast.Expr {
return args
}
- if isTestingTPtr(pass, args[0]) {
+ if implementsTestingT(pass, args[0]) {
return args[1:]
}
return args
}
-
-func isTestingTPtr(pass *analysis.Pass, arg ast.Expr) bool {
- assertTestingTObj := analysisutil.ObjectOf(pass.Pkg, testify.AssertPkgPath, "TestingT")
- requireTestingTObj := analysisutil.ObjectOf(pass.Pkg, testify.RequirePkgPath, "TestingT")
-
- argType := pass.TypesInfo.TypeOf(arg)
- if argType == nil {
- return false
- }
-
- return ((assertTestingTObj != nil) &&
- types.Implements(argType, assertTestingTObj.Type().Underlying().(*types.Interface))) ||
- ((requireTestingTObj != nil) &&
- types.Implements(argType, requireTestingTObj.Type().Underlying().(*types.Interface)))
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go
index e34a21bf9..17c7d14ee 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go
@@ -11,6 +11,7 @@ var registry = checkersRegistry{
{factory: asCheckerFactory(NewBoolCompare), enabledByDefault: true},
{factory: asCheckerFactory(NewEmpty), enabledByDefault: true},
{factory: asCheckerFactory(NewLen), enabledByDefault: true},
+ {factory: asCheckerFactory(NewNegativePositive), enabledByDefault: true},
{factory: asCheckerFactory(NewCompares), enabledByDefault: true},
{factory: asCheckerFactory(NewErrorNil), enabledByDefault: true},
{factory: asCheckerFactory(NewNilCompare), enabledByDefault: true},
@@ -19,10 +20,13 @@ var registry = checkersRegistry{
{factory: asCheckerFactory(NewSuiteExtraAssertCall), enabledByDefault: true},
{factory: asCheckerFactory(NewSuiteDontUsePkg), enabledByDefault: true},
{factory: asCheckerFactory(NewUselessAssert), enabledByDefault: true},
+ {factory: asCheckerFactory(NewFormatter), enabledByDefault: true},
// Advanced checkers.
{factory: asCheckerFactory(NewBlankImport), enabledByDefault: true},
{factory: asCheckerFactory(NewGoRequire), enabledByDefault: true},
{factory: asCheckerFactory(NewRequireError), enabledByDefault: true},
+ {factory: asCheckerFactory(NewSuiteBrokenParallel), enabledByDefault: true},
+ {factory: asCheckerFactory(NewSuiteSubtestRun), enabledByDefault: true},
{factory: asCheckerFactory(NewSuiteTHelper), enabledByDefault: false},
}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/compares.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/compares.go
index 336a34512..bdde03d95 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/compares.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/compares.go
@@ -1,13 +1,10 @@
package checkers
import (
- "bytes"
"go/ast"
"go/token"
"golang.org/x/tools/go/analysis"
-
- "github.com/Antonboom/testifylint/internal/analysisutil"
)
// Compares detects situations like
@@ -29,6 +26,9 @@ import (
// assert.GreaterOrEqual(t, a, b)
// assert.Less(t, a, b)
// assert.LessOrEqual(t, a, b)
+//
+// If `a` and `b` are pointers then `assert.Same`/`NotSame` is required instead,
+// due to the inappropriate recursive nature of `assert.Equal` (based on `reflect.DeepEqual`).
type Compares struct{}
// NewCompares constructs Compares checker.
@@ -56,17 +56,28 @@ func (checker Compares) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia
return nil
}
- if proposedFn, ok := tokenToProposedFn[be.Op]; ok {
- a, b := be.X, be.Y
- return newUseFunctionDiagnostic(checker.Name(), call, proposedFn,
- newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{
- Pos: be.X.Pos(),
- End: be.Y.End(),
- NewText: formatAsCallArgs(pass, a, b),
- }),
- )
+ proposedFn, ok := tokenToProposedFn[be.Op]
+ if !ok {
+ return nil
}
- return nil
+
+ if isPointer(pass, be.X) && isPointer(pass, be.Y) {
+ switch proposedFn {
+ case "Equal":
+ proposedFn = "Same"
+ case "NotEqual":
+ proposedFn = "NotSame"
+ }
+ }
+
+ a, b := be.X, be.Y
+ return newUseFunctionDiagnostic(checker.Name(), call, proposedFn,
+ newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{
+ Pos: be.X.Pos(),
+ End: be.Y.End(),
+ NewText: formatAsCallArgs(pass, a, b),
+ }),
+ )
}
var tokenToProposedFnInsteadOfTrue = map[token.Token]string{
@@ -86,11 +97,3 @@ var tokenToProposedFnInsteadOfFalse = map[token.Token]string{
token.LSS: "GreaterOrEqual",
token.LEQ: "Greater",
}
-
-// formatAsCallArgs joins a and b and return bytes like `a, b`.
-func formatAsCallArgs(pass *analysis.Pass, a, b ast.Node) []byte {
- return bytes.Join([][]byte{
- analysisutil.NodeBytes(pass.Fset, a),
- analysisutil.NodeBytes(pass.Fset, b),
- }, []byte(", "))
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go
index 5ad371bb4..eafecb678 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go
@@ -1,10 +1,8 @@
package checkers
import (
- "fmt"
"go/ast"
"go/token"
- "go/types"
"golang.org/x/tools/go/analysis"
@@ -23,11 +21,16 @@ import (
// assert.Greater(t, 0, len(arr))
// assert.Less(t, len(arr), 1)
// assert.Greater(t, 1, len(arr))
+// assert.Zero(t, len(arr))
+// assert.Empty(t, len(arr))
//
// assert.NotEqual(t, 0, len(arr))
// assert.NotEqualValues(t, 0, len(arr))
// assert.Less(t, 0, len(arr))
// assert.Greater(t, len(arr), 0)
+// assert.Positive(t, len(arr))
+// assert.NotZero(t, len(arr))
+// assert.NotEmpty(t, len(arr))
//
// and requires
//
@@ -58,10 +61,23 @@ func (checker Empty) checkEmpty(pass *analysis.Pass, call *CallMeta) *analysis.D
)
}
+ if len(call.Args) == 0 {
+ return nil
+ }
+
+ a := call.Args[0]
+ switch call.Fn.NameFTrimmed {
+ case "Zero", "Empty":
+ lenArg, ok := isBuiltinLenCall(pass, a)
+ if ok {
+ return newUseEmptyDiagnostic(a.Pos(), a.End(), lenArg)
+ }
+ }
+
if len(call.Args) < 2 {
return nil
}
- a, b := call.Args[0], call.Args[1]
+ b := call.Args[1]
switch call.Fn.NameFTrimmed {
case "Len":
@@ -112,10 +128,23 @@ func (checker Empty) checkNotEmpty(pass *analysis.Pass, call *CallMeta) *analysi
)
}
+ if len(call.Args) == 0 {
+ return nil
+ }
+
+ a := call.Args[0]
+ switch call.Fn.NameFTrimmed {
+ case "NotZero", "NotEmpty", "Positive":
+ lenArg, ok := isBuiltinLenCall(pass, a)
+ if ok {
+ return newUseNotEmptyDiagnostic(a.Pos(), a.End(), lenArg)
+ }
+ }
+
if len(call.Args) < 2 {
return nil
}
- a, b := call.Args[0], call.Args[1]
+ b := call.Args[1]
switch call.Fn.NameFTrimmed {
case "NotEqual", "NotEqualValues":
@@ -138,35 +167,3 @@ func (checker Empty) checkNotEmpty(pass *analysis.Pass, call *CallMeta) *analysi
}
return nil
}
-
-var lenObj = types.Universe.Lookup("len")
-
-func isLenCallAndZero(pass *analysis.Pass, a, b ast.Expr) (ast.Expr, bool) {
- lenArg, ok := isBuiltinLenCall(pass, a)
- return lenArg, ok && isZero(b)
-}
-
-func isBuiltinLenCall(pass *analysis.Pass, e ast.Expr) (ast.Expr, bool) {
- ce, ok := e.(*ast.CallExpr)
- if !ok {
- return nil, false
- }
-
- if analysisutil.IsObj(pass.TypesInfo, ce.Fun, lenObj) && len(ce.Args) == 1 {
- return ce.Args[0], true
- }
- return nil, false
-}
-
-func isZero(e ast.Expr) bool {
- return isIntNumber(e, 0)
-}
-
-func isOne(e ast.Expr) bool {
- return isIntNumber(e, 1)
-}
-
-func isIntNumber(e ast.Expr, v int) bool {
- bl, ok := e.(*ast.BasicLit)
- return ok && bl.Kind == token.INT && bl.Value == fmt.Sprintf("%d", v)
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/error_is_as.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/error_is_as.go
index 0363873a6..ab92c2ec0 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/error_is_as.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/error_is_as.go
@@ -6,8 +6,6 @@ import (
"go/types"
"golang.org/x/tools/go/analysis"
-
- "github.com/Antonboom/testifylint/internal/analysisutil"
)
// ErrorIsAs detects situations like
@@ -142,25 +140,3 @@ func (checker ErrorIsAs) Check(pass *analysis.Pass, call *CallMeta) *analysis.Di
}
return nil
}
-
-func isErrorsIsCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
- return isErrorsPkgFnCall(pass, ce, "Is")
-}
-
-func isErrorsAsCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
- return isErrorsPkgFnCall(pass, ce, "As")
-}
-
-func isErrorsPkgFnCall(pass *analysis.Pass, ce *ast.CallExpr, fn string) bool {
- se, ok := ce.Fun.(*ast.SelectorExpr)
- if !ok {
- return false
- }
-
- errorsIsObj := analysisutil.ObjectOf(pass.Pkg, "errors", fn)
- if errorsIsObj == nil {
- return false
- }
-
- return analysisutil.IsObj(pass.TypesInfo, se.Sel, errorsIsObj)
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/error_nil.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/error_nil.go
index 5b0af7458..1e56d222a 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/error_nil.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/error_nil.go
@@ -3,7 +3,6 @@ package checkers
import (
"go/ast"
"go/token"
- "go/types"
"golang.org/x/tools/go/analysis"
@@ -91,23 +90,3 @@ func (checker ErrorNil) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia
}
return nil
}
-
-var (
- errorType = types.Universe.Lookup("error").Type()
- errorIface = errorType.Underlying().(*types.Interface)
-)
-
-func isError(pass *analysis.Pass, expr ast.Expr) bool {
- t := pass.TypesInfo.TypeOf(expr)
- if t == nil {
- return false
- }
-
- _, ok := t.Underlying().(*types.Interface)
- return ok && types.Implements(t, errorIface)
-}
-
-func isNil(expr ast.Expr) bool {
- ident, ok := expr.(*ast.Ident)
- return ok && ident.Name == "nil"
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go
index e6825eaa6..77784dc7b 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go
@@ -3,7 +3,6 @@ package checkers
import (
"go/ast"
"go/token"
- "go/types"
"regexp"
"golang.org/x/tools/go/analysis"
@@ -15,7 +14,7 @@ import (
var DefaultExpectedVarPattern = regexp.MustCompile(
`(^(exp(ected)?|want(ed)?)([A-Z]\w*)?$)|(^(\w*[a-z])?(Exp(ected)?|Want(ed)?)$)`)
-// ExpectedActual detects situation like
+// ExpectedActual detects situations like
//
// assert.Equal(t, result, expected)
// assert.EqualExportedValues(t, resultObj, User{Name: "Anton"})
@@ -131,9 +130,9 @@ func (checker ExpectedActual) isExpectedValueCandidate(pass *analysis.Pass, expr
return isBasicLit(expr) ||
isUntypedConst(pass, expr) ||
isTypedConst(pass, expr) ||
- isIdentNamedAsExpected(checker.expVarPattern, expr) ||
- isStructVarNamedAsExpected(checker.expVarPattern, expr) ||
- isStructFieldNamedAsExpected(checker.expVarPattern, expr)
+ isIdentNamedAfterPattern(checker.expVarPattern, expr) ||
+ isStructVarNamedAfterPattern(checker.expVarPattern, expr) ||
+ isStructFieldNamedAfterPattern(checker.expVarPattern, expr)
}
func isParenExpr(ce *ast.CallExpr) bool {
@@ -159,7 +158,7 @@ func isCastedBasicLitOrExpectedValue(ce *ast.CallExpr, pattern *regexp.Regexp) b
"int", "int8", "int16", "int32", "int64",
"float32", "float64",
"rune", "string":
- return isBasicLit(ce.Args[0]) || isIdentNamedAsExpected(pattern, ce.Args[0])
+ return isBasicLit(ce.Args[0]) || isIdentNamedAfterPattern(pattern, ce.Args[0])
}
return false
}
@@ -178,38 +177,3 @@ func isExpectedValueFactory(pass *analysis.Pass, ce *ast.CallExpr, pattern *rege
}
return false
}
-
-func isBasicLit(e ast.Expr) bool {
- _, ok := e.(*ast.BasicLit)
- return ok
-}
-
-func isUntypedConst(p *analysis.Pass, e ast.Expr) bool {
- t := p.TypesInfo.TypeOf(e)
- if t == nil {
- return false
- }
-
- b, ok := t.(*types.Basic)
- return ok && b.Info()&types.IsUntyped > 0
-}
-
-func isTypedConst(p *analysis.Pass, e ast.Expr) bool {
- tt, ok := p.TypesInfo.Types[e]
- return ok && tt.IsValue() && tt.Value != nil
-}
-
-func isIdentNamedAsExpected(pattern *regexp.Regexp, e ast.Expr) bool {
- id, ok := e.(*ast.Ident)
- return ok && pattern.MatchString(id.Name)
-}
-
-func isStructVarNamedAsExpected(pattern *regexp.Regexp, e ast.Expr) bool {
- s, ok := e.(*ast.SelectorExpr)
- return ok && isIdentNamedAsExpected(pattern, s.X)
-}
-
-func isStructFieldNamedAsExpected(pattern *regexp.Regexp, e ast.Expr) bool {
- s, ok := e.(*ast.SelectorExpr)
- return ok && isIdentNamedAsExpected(pattern, s.Sel)
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go
index 10b1330de..7436f9ca1 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go
@@ -2,14 +2,12 @@ package checkers
import (
"fmt"
- "go/ast"
"go/token"
- "go/types"
"golang.org/x/tools/go/analysis"
)
-// FloatCompare detects situation like
+// FloatCompare detects situations like
//
// assert.Equal(t, 42.42, result)
// assert.EqualValues(t, 42.42, result)
@@ -33,10 +31,10 @@ func (checker FloatCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis
return len(call.Args) > 1 && (isFloat(pass, call.Args[0]) || isFloat(pass, call.Args[1]))
case "True":
- return len(call.Args) > 0 && isFloatCompare(pass, call.Args[0], token.EQL)
+ return len(call.Args) > 0 && isComparisonWithFloat(pass, call.Args[0], token.EQL)
case "False":
- return len(call.Args) > 0 && isFloatCompare(pass, call.Args[0], token.NEQ)
+ return len(call.Args) > 0 && isComparisonWithFloat(pass, call.Args[0], token.NEQ)
}
return false
}()
@@ -50,21 +48,3 @@ func (checker FloatCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis
}
return nil
}
-
-func isFloat(pass *analysis.Pass, expr ast.Expr) bool {
- t := pass.TypesInfo.TypeOf(expr)
- if t == nil {
- return false
- }
-
- bt, ok := t.Underlying().(*types.Basic)
- return ok && (bt.Info()&types.IsFloat > 0)
-}
-
-func isFloatCompare(p *analysis.Pass, e ast.Expr, op token.Token) bool {
- be, ok := e.(*ast.BinaryExpr)
- if !ok {
- return false
- }
- return be.Op == op && (isFloat(p, be.X) || isFloat(p, be.Y))
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go
new file mode 100644
index 000000000..3401bb097
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go
@@ -0,0 +1,187 @@
+package checkers
+
+import (
+ "fmt"
+ "go/ast"
+ "go/types"
+ "strconv"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+ "github.com/Antonboom/testifylint/internal/checkers/printf"
+ "github.com/Antonboom/testifylint/internal/testify"
+)
+
+// Formatter detects situations like
+//
+// assert.ElementsMatch(t, certConfig.Org, csr.Subject.Org, "organizations not equal")
+// assert.Error(t, err, fmt.Sprintf("Profile %s should not be valid", test.profile))
+// assert.Errorf(t, err, fmt.Sprintf("test %s", test.testName))
+// assert.Truef(t, targetTs.Equal(ts), "the timestamp should be as expected (%s) but was %s", targetTs)
+// ...
+//
+// and requires
+//
+// assert.ElementsMatchf(t, certConfig.Org, csr.Subject.Org, "organizations not equal")
+// assert.Errorf(t, err, "Profile %s should not be valid", test.profile)
+// assert.Errorf(t, err, "test %s", test.testName)
+// assert.Truef(t, targetTs.Equal(ts), "the timestamp should be as expected (%s) but was %s", targetTs, ts)
+type Formatter struct {
+ checkFormatString bool
+ requireFFuncs bool
+}
+
+// NewFormatter constructs Formatter checker.
+func NewFormatter() *Formatter {
+ return &Formatter{
+ checkFormatString: true,
+ requireFFuncs: false,
+ }
+}
+
+func (Formatter) Name() string { return "formatter" }
+
+func (checker *Formatter) SetCheckFormatString(v bool) *Formatter {
+ checker.checkFormatString = v
+ return checker
+}
+
+func (checker *Formatter) SetRequireFFuncs(v bool) *Formatter {
+ checker.requireFFuncs = v
+ return checker
+}
+
+func (checker Formatter) Check(pass *analysis.Pass, call *CallMeta) (result *analysis.Diagnostic) {
+ if call.Fn.IsFmt {
+ return checker.checkFmtAssertion(pass, call)
+ }
+ return checker.checkNotFmtAssertion(pass, call)
+}
+
+func (checker Formatter) checkNotFmtAssertion(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic {
+ msgAndArgsPos, ok := isPrintfLikeCall(pass, call, call.Fn.Signature)
+ if !ok {
+ return nil
+ }
+
+ fFunc := call.Fn.Name + "f"
+
+ if msgAndArgsPos == len(call.ArgsRaw)-1 {
+ msgAndArgs := call.ArgsRaw[msgAndArgsPos]
+ if args, ok := isFmtSprintfCall(pass, msgAndArgs); ok {
+ if checker.requireFFuncs {
+ msg := fmt.Sprintf("remove unnecessary fmt.Sprintf and use %s.%s", call.SelectorXStr, fFunc)
+ return newDiagnostic(checker.Name(), call, msg,
+ newSuggestedFuncReplacement(call, fFunc, analysis.TextEdit{
+ Pos: msgAndArgs.Pos(),
+ End: msgAndArgs.End(),
+ NewText: formatAsCallArgs(pass, args...),
+ }),
+ )
+ }
+ return newRemoveSprintfDiagnostic(pass, checker.Name(), call, msgAndArgs, args)
+ }
+ }
+
+ if checker.requireFFuncs {
+ return newUseFunctionDiagnostic(checker.Name(), call, fFunc, newSuggestedFuncReplacement(call, fFunc))
+ }
+ return nil
+}
+
+func (checker Formatter) checkFmtAssertion(pass *analysis.Pass, call *CallMeta) (result *analysis.Diagnostic) {
+ formatPos := getMsgPosition(call.Fn.Signature)
+ if formatPos < 0 {
+ return nil
+ }
+
+ msg := call.ArgsRaw[formatPos]
+
+ if formatPos == len(call.ArgsRaw)-1 {
+ if args, ok := isFmtSprintfCall(pass, msg); ok {
+ return newRemoveSprintfDiagnostic(pass, checker.Name(), call, msg, args)
+ }
+ }
+
+ if checker.checkFormatString {
+ report := pass.Report
+ defer func() { pass.Report = report }()
+
+ pass.Report = func(d analysis.Diagnostic) {
+ result = newDiagnostic(checker.Name(), call, d.Message, nil)
+ }
+
+ format, err := strconv.Unquote(analysisutil.NodeString(pass.Fset, msg))
+ if err != nil {
+ return nil
+ }
+ printf.CheckPrintf(pass, call.Call, call.String(), format, formatPos)
+ }
+ return result
+}
+
+func isPrintfLikeCall(pass *analysis.Pass, call *CallMeta, sig *types.Signature) (int, bool) {
+ msgAndArgsPos := getMsgAndArgsPosition(sig)
+ if msgAndArgsPos < 0 {
+ return -1, false
+ }
+
+ fmtFn := analysisutil.ObjectOf(pass.Pkg, testify.AssertPkgPath, call.Fn.Name+"f")
+ if fmtFn == nil {
+ // NOTE(a.telyshev): No formatted analogue of assertion.
+ return -1, false
+ }
+
+ return msgAndArgsPos, len(call.ArgsRaw) > msgAndArgsPos
+}
+
+func getMsgAndArgsPosition(sig *types.Signature) int {
+ params := sig.Params()
+ if params.Len() < 1 {
+ return -1
+ }
+
+ lastIdx := params.Len() - 1
+ lastParam := params.At(lastIdx)
+
+ _, isSlice := lastParam.Type().(*types.Slice)
+ if lastParam.Name() == "msgAndArgs" && isSlice {
+ return lastIdx
+ }
+ return -1
+}
+
+func getMsgPosition(sig *types.Signature) int {
+ for i := 0; i < sig.Params().Len(); i++ {
+ param := sig.Params().At(i)
+
+ if b, ok := param.Type().(*types.Basic); ok && b.Kind() == types.String && param.Name() == "msg" {
+ return i
+ }
+ }
+ return -1
+}
+
+func isFmtSprintfCall(pass *analysis.Pass, expr ast.Expr) ([]ast.Expr, bool) {
+ ce, ok := expr.(*ast.CallExpr)
+ if !ok {
+ return nil, false
+ }
+
+ se, ok := ce.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return nil, false
+ }
+
+ sprintfObj := analysisutil.ObjectOf(pass.Pkg, "fmt", "Sprintf")
+ if sprintfObj == nil {
+ return nil, false
+ }
+
+ if !analysisutil.IsObj(pass.TypesInfo, se.Sel, sprintfObj) {
+ return nil, false
+ }
+
+ return ce.Args, true
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/go_require.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/go_require.go
index 0844f15a0..060c96033 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/go_require.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/go_require.go
@@ -12,8 +12,9 @@ import (
)
const (
- goRequireFnReportFormat = "%s contains assertions that must only be used in the goroutine running the test function"
- goRequireCallReportFormat = "%s must only be used in the goroutine running the test function"
+ goRequireFnReportFormat = "%s contains assertions that must only be used in the goroutine running the test function"
+ goRequireCallReportFormat = "%s must only be used in the goroutine running the test function"
+ goRequireHTTPHandlerReportFormat = "do not use %s in http handlers"
)
// GoRequire takes idea from go vet's "testinggoroutine" check
@@ -27,12 +28,19 @@ const (
// assert.FailNow(t, msg)
// }
// }()
-type GoRequire struct{}
+type GoRequire struct {
+ ignoreHTTPHandlers bool
+}
// NewGoRequire constructs GoRequire checker.
-func NewGoRequire() GoRequire { return GoRequire{} }
+func NewGoRequire() *GoRequire { return new(GoRequire) }
func (GoRequire) Name() string { return "go-require" }
+func (checker *GoRequire) SetIgnoreHTTPHandlers(v bool) *GoRequire {
+ checker.ignoreHTTPHandlers = v
+ return checker
+}
+
// Check should be consistent with
// https://cs.opensource.google/go/x/tools/+/master:go/analysis/passes/testinggoroutine/testinggoroutine.go
//
@@ -162,6 +170,45 @@ func (checker GoRequire) Check(pass *analysis.Pass, inspector *inspector.Inspect
return true
})
+ if !checker.ignoreHTTPHandlers {
+ diagnostics = append(diagnostics, checker.checkHTTPHandlers(pass, inspector)...)
+ }
+
+ return diagnostics
+}
+
+func (checker GoRequire) checkHTTPHandlers(pass *analysis.Pass, insp *inspector.Inspector) (diagnostics []analysis.Diagnostic) {
+ insp.WithStack([]ast.Node{(*ast.CallExpr)(nil)}, func(node ast.Node, push bool, stack []ast.Node) bool {
+ if !push {
+ return false
+ }
+ if len(stack) < 3 {
+ return true
+ }
+
+ fID := findSurroundingFunc(pass, stack)
+ if fID == nil || !fID.meta.isHTTPHandler {
+ return true
+ }
+
+ testifyCall := NewCallMeta(pass, node.(*ast.CallExpr))
+ if testifyCall == nil {
+ return true
+ }
+
+ switch checker.checkCall(testifyCall) {
+ case goRequireVerdictRequire:
+ d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, "require"), nil)
+ diagnostics = append(diagnostics, *d)
+
+ case goRequireVerdictAssertFailNow:
+ d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, testifyCall), nil)
+ diagnostics = append(diagnostics, *d)
+
+ case goRequireVerdictNoExit:
+ }
+ return false
+ })
return diagnostics
}
@@ -296,32 +343,3 @@ func (s boolStack) Last() bool {
}
return s[n-1]
}
-
-func isSubTestRun(pass *analysis.Pass, ce *ast.CallExpr) bool {
- se, ok := ce.Fun.(*ast.SelectorExpr)
- if !ok || se.Sel == nil {
- return false
- }
- return (isTestingTPtr(pass, se.X) || implementsTestifySuiteIface(pass, se.X)) && se.Sel.Name == "Run"
-}
-
-func isTestingFuncOrMethod(pass *analysis.Pass, fd *ast.FuncDecl) bool {
- return hasTestingTParam(pass, fd.Type) || isTestifySuiteMethod(pass, fd)
-}
-
-func isTestingAnonymousFunc(pass *analysis.Pass, ft *ast.FuncType) bool {
- return hasTestingTParam(pass, ft)
-}
-
-func hasTestingTParam(pass *analysis.Pass, ft *ast.FuncType) bool {
- if ft == nil || ft.Params == nil {
- return false
- }
-
- for _, param := range ft.Params.List {
- if isTestingTPtr(pass, param.Type) {
- return true
- }
- }
- return false
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers.go
new file mode 100644
index 000000000..4e4735269
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers.go
@@ -0,0 +1,43 @@
+package checkers
+
+import (
+ "go/ast"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+func xor(a, b bool) bool {
+ return a != b
+}
+
+// anyVal returns the first value[i] for which bools[i] is true.
+func anyVal[T any](bools []bool, vals ...T) (T, bool) {
+ if len(bools) != len(vals) {
+ panic("inconsistent usage of valOr") //nolint:forbidigo // Does not depend on the code being analyzed.
+ }
+
+ for i, b := range bools {
+ if b {
+ return vals[i], true
+ }
+ }
+
+ var _default T
+ return _default, false
+}
+
+func anyCondSatisfaction(pass *analysis.Pass, p predicate, vals ...ast.Expr) bool {
+ for _, v := range vals {
+ if p(pass, v) {
+ return true
+ }
+ }
+ return false
+}
+
+// p transforms simple is-function in a predicate.
+func p(fn func(e ast.Expr) bool) predicate {
+ return func(_ *analysis.Pass, e ast.Expr) bool {
+ return fn(e)
+ }
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go
new file mode 100644
index 000000000..432a3032c
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go
@@ -0,0 +1,113 @@
+package checkers
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+func isZero(e ast.Expr) bool { return isIntNumber(e, 0) }
+
+func isOne(e ast.Expr) bool { return isIntNumber(e, 1) }
+
+func isAnyZero(e ast.Expr) bool {
+ return isIntNumber(e, 0) || isTypedSignedIntNumber(e, 0) || isTypedUnsignedIntNumber(e, 0)
+}
+
+func isNotAnyZero(e ast.Expr) bool {
+ return !isAnyZero(e)
+}
+
+func isZeroOrSignedZero(e ast.Expr) bool {
+ return isIntNumber(e, 0) || isTypedSignedIntNumber(e, 0)
+}
+
+func isSignedNotZero(pass *analysis.Pass, e ast.Expr) bool {
+ return !isUnsigned(pass, e) && !isZeroOrSignedZero(e)
+}
+
+func isTypedSignedIntNumber(e ast.Expr, v int) bool {
+ return isTypedIntNumber(e, v, "int", "int8", "int16", "int32", "int64")
+}
+
+func isTypedUnsignedIntNumber(e ast.Expr, v int) bool {
+ return isTypedIntNumber(e, v, "uint", "uint8", "uint16", "uint32", "uint64")
+}
+
+func isTypedIntNumber(e ast.Expr, v int, types ...string) bool {
+ ce, ok := e.(*ast.CallExpr)
+ if !ok || len(ce.Args) != 1 {
+ return false
+ }
+
+ fn, ok := ce.Fun.(*ast.Ident)
+ if !ok {
+ return false
+ }
+
+ for _, t := range types {
+ if fn.Name == t {
+ return isIntNumber(ce.Args[0], v)
+ }
+ }
+ return false
+}
+
+func isIntNumber(e ast.Expr, v int) bool {
+ bl, ok := e.(*ast.BasicLit)
+ return ok && bl.Kind == token.INT && bl.Value == fmt.Sprintf("%d", v)
+}
+
+func isBasicLit(e ast.Expr) bool {
+ _, ok := e.(*ast.BasicLit)
+ return ok
+}
+
+func isIntBasicLit(e ast.Expr) bool {
+ bl, ok := e.(*ast.BasicLit)
+ return ok && bl.Kind == token.INT
+}
+
+func isUntypedConst(pass *analysis.Pass, e ast.Expr) bool {
+ return isUnderlying(pass, e, types.IsUntyped)
+}
+
+func isTypedConst(pass *analysis.Pass, e ast.Expr) bool {
+ tt, ok := pass.TypesInfo.Types[e]
+ return ok && tt.IsValue() && tt.Value != nil
+}
+
+func isFloat(pass *analysis.Pass, e ast.Expr) bool {
+ return isUnderlying(pass, e, types.IsFloat)
+}
+
+func isUnsigned(pass *analysis.Pass, e ast.Expr) bool {
+ return isUnderlying(pass, e, types.IsUnsigned)
+}
+
+func isUnderlying(pass *analysis.Pass, e ast.Expr, flag types.BasicInfo) bool {
+ t := pass.TypesInfo.TypeOf(e)
+ if t == nil {
+ return false
+ }
+
+ bt, ok := t.Underlying().(*types.Basic)
+ return ok && (bt.Info()&flag > 0)
+}
+
+func isPointer(pass *analysis.Pass, e ast.Expr) bool {
+ _, ok := pass.TypesInfo.TypeOf(e).(*types.Pointer)
+ return ok
+}
+
+// untype returns v from type(v) expression or v itself if there is no type cast.
+func untype(e ast.Expr) ast.Expr {
+ ce, ok := e.(*ast.CallExpr)
+ if !ok || len(ce.Args) != 1 {
+ return e
+ }
+ return ce.Args[0]
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_bool.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_bool.go
new file mode 100644
index 000000000..13e579a2b
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_bool.go
@@ -0,0 +1,33 @@
+package checkers
+
+import (
+ "go/ast"
+ "go/types"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+var (
+ falseObj = types.Universe.Lookup("false")
+ trueObj = types.Universe.Lookup("true")
+)
+
+func isUntypedTrue(pass *analysis.Pass, e ast.Expr) bool {
+ return analysisutil.IsObj(pass.TypesInfo, e, trueObj)
+}
+
+func isUntypedFalse(pass *analysis.Pass, e ast.Expr) bool {
+ return analysisutil.IsObj(pass.TypesInfo, e, falseObj)
+}
+
+func isBuiltinBool(pass *analysis.Pass, e ast.Expr) bool {
+ basicType, ok := pass.TypesInfo.TypeOf(e).(*types.Basic)
+ return ok && basicType.Kind() == types.Bool
+}
+
+func isBoolOverride(pass *analysis.Pass, e ast.Expr) bool {
+ namedType, ok := pass.TypesInfo.TypeOf(e).(*types.Named)
+ return ok && namedType.Obj().Name() == "bool"
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_comparison.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_comparison.go
new file mode 100644
index 000000000..ac11d7399
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_comparison.go
@@ -0,0 +1,68 @@
+package checkers
+
+import (
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+func isComparisonWithFloat(p *analysis.Pass, e ast.Expr, op token.Token) bool {
+ be, ok := e.(*ast.BinaryExpr)
+ if !ok {
+ return false
+ }
+ return be.Op == op && (isFloat(p, be.X) || isFloat(p, be.Y))
+}
+
+func isComparisonWithTrue(pass *analysis.Pass, e ast.Expr, op token.Token) (ast.Expr, bool) {
+ return isComparisonWith(pass, e, isUntypedTrue, op)
+}
+
+func isComparisonWithFalse(pass *analysis.Pass, e ast.Expr, op token.Token) (ast.Expr, bool) {
+ return isComparisonWith(pass, e, isUntypedFalse, op)
+}
+
+type predicate func(pass *analysis.Pass, e ast.Expr) bool
+
+func isComparisonWith(
+ pass *analysis.Pass,
+ e ast.Expr,
+ predicate predicate,
+ op token.Token,
+) (ast.Expr, bool) {
+ be, ok := e.(*ast.BinaryExpr)
+ if !ok {
+ return nil, false
+ }
+ if be.Op != op {
+ return nil, false
+ }
+
+ t1, t2 := predicate(pass, be.X), predicate(pass, be.Y)
+ if xor(t1, t2) {
+ if t1 {
+ return be.Y, true
+ }
+ return be.X, true
+ }
+ return nil, false
+}
+
+func isStrictComparisonWith(
+ pass *analysis.Pass,
+ e ast.Expr,
+ lhs predicate,
+ op token.Token,
+ rhs predicate,
+) (ast.Expr, ast.Expr, bool) {
+ be, ok := e.(*ast.BinaryExpr)
+ if !ok {
+ return nil, nil, false
+ }
+
+ if be.Op == op && lhs(pass, be.X) && rhs(pass, be.Y) {
+ return be.X, be.Y, true
+ }
+ return nil, nil, false
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_context.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_context.go
new file mode 100644
index 000000000..e8505fad0
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_context.go
@@ -0,0 +1,126 @@
+package checkers
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+type funcID struct {
+ pos token.Pos
+ posStr string
+ name string
+ meta funcMeta
+}
+
+type funcMeta struct {
+ isTestCleanup bool
+ isGoroutine bool
+ isHTTPHandler bool
+}
+
+func (id funcID) String() string {
+ return fmt.Sprintf("%s at %s", id.name, id.posStr)
+}
+
+func findSurroundingFunc(pass *analysis.Pass, stack []ast.Node) *funcID {
+ for i := len(stack) - 2; i >= 0; i-- {
+ var fType *ast.FuncType
+ var fName string
+ var isTestCleanup bool
+ var isGoroutine bool
+ var isHTTPHandler bool
+
+ switch fd := stack[i].(type) {
+ case *ast.FuncDecl:
+ fType, fName = fd.Type, fd.Name.Name
+
+ if isSuiteMethod(pass, fd) {
+ if ident := fd.Name; ident != nil && isSuiteAfterTestMethod(ident.Name) {
+ isTestCleanup = true
+ }
+ }
+
+ if mimicHTTPHandler(pass, fd.Type) {
+ isHTTPHandler = true
+ }
+
+ case *ast.FuncLit:
+ fType, fName = fd.Type, "anonymous"
+
+ if mimicHTTPHandler(pass, fType) {
+ isHTTPHandler = true
+ }
+
+ if i >= 2 { //nolint:nestif
+ if ce, ok := stack[i-1].(*ast.CallExpr); ok {
+ if se, ok := ce.Fun.(*ast.SelectorExpr); ok {
+ isTestCleanup = implementsTestingT(pass, se.X) && se.Sel != nil && (se.Sel.Name == "Cleanup")
+ }
+
+ if _, ok := stack[i-2].(*ast.GoStmt); ok {
+ isGoroutine = true
+ }
+ }
+ }
+
+ default:
+ continue
+ }
+
+ return &funcID{
+ pos: fType.Pos(),
+ posStr: pass.Fset.Position(fType.Pos()).String(),
+ name: fName,
+ meta: funcMeta{
+ isTestCleanup: isTestCleanup,
+ isGoroutine: isGoroutine,
+ isHTTPHandler: isHTTPHandler,
+ },
+ }
+ }
+ return nil
+}
+
+func findNearestNode[T ast.Node](stack []ast.Node) (v T) {
+ v, _ = findNearestNodeWithIdx[T](stack)
+ return
+}
+
+func findNearestNodeWithIdx[T ast.Node](stack []ast.Node) (v T, index int) {
+ for i := len(stack) - 2; i >= 0; i-- {
+ if n, ok := stack[i].(T); ok {
+ return n, i
+ }
+ }
+ return
+}
+
+func fnContainsAssertions(pass *analysis.Pass, fn *ast.FuncDecl) bool {
+ if fn.Body == nil {
+ return false
+ }
+
+ for _, s := range fn.Body.List {
+ if isAssertionStmt(pass, s) {
+ return true
+ }
+ }
+ return false
+}
+
+func isAssertionStmt(pass *analysis.Pass, stmt ast.Stmt) bool {
+ expr, ok := stmt.(*ast.ExprStmt)
+ if !ok {
+ return false
+ }
+
+ ce, ok := expr.X.(*ast.CallExpr)
+ if !ok {
+ return false
+ }
+
+ return NewCallMeta(pass, ce) != nil
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/diagnostic.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go
index 4ab69c69b..3ae88a560 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/diagnostic.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go
@@ -2,6 +2,7 @@ package checkers
import (
"fmt"
+ "go/ast"
"golang.org/x/tools/go/analysis"
)
@@ -21,6 +22,25 @@ func newUseFunctionDiagnostic(
return newDiagnostic(checker, call, msg, fix)
}
+func newRemoveSprintfDiagnostic(
+ pass *analysis.Pass,
+ checker string,
+ call *CallMeta,
+ sprintfPos analysis.Range,
+ sprintfArgs []ast.Expr,
+) *analysis.Diagnostic {
+ return newDiagnostic(checker, call, "remove unnecessary fmt.Sprintf", &analysis.SuggestedFix{
+ Message: "Remove `fmt.Sprintf`",
+ TextEdits: []analysis.TextEdit{
+ {
+ Pos: sprintfPos.Pos(),
+ End: sprintfPos.End(),
+ NewText: formatAsCallArgs(pass, sprintfArgs...),
+ },
+ },
+ })
+}
+
func newDiagnostic(
checker string,
rng analysis.Range,
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_error.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_error.go
new file mode 100644
index 000000000..55cd5fd05
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_error.go
@@ -0,0 +1,42 @@
+package checkers
+
+import (
+ "go/ast"
+ "go/types"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+var (
+ errorObj = types.Universe.Lookup("error")
+ errorType = errorObj.Type()
+ errorIface = errorType.Underlying().(*types.Interface)
+)
+
+func isError(pass *analysis.Pass, expr ast.Expr) bool {
+ return pass.TypesInfo.TypeOf(expr) == errorType
+}
+
+func isErrorsIsCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
+ return isErrorsPkgFnCall(pass, ce, "Is")
+}
+
+func isErrorsAsCall(pass *analysis.Pass, ce *ast.CallExpr) bool {
+ return isErrorsPkgFnCall(pass, ce, "As")
+}
+
+func isErrorsPkgFnCall(pass *analysis.Pass, ce *ast.CallExpr, fn string) bool {
+ se, ok := ce.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return false
+ }
+
+ errorsIsObj := analysisutil.ObjectOf(pass.Pkg, "errors", fn)
+ if errorsIsObj == nil {
+ return false
+ }
+
+ return analysisutil.IsObj(pass.TypesInfo, se.Sel, errorsIsObj)
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go
new file mode 100644
index 000000000..765fce527
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go
@@ -0,0 +1,26 @@
+package checkers
+
+import (
+ "bytes"
+ "go/ast"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+// formatAsCallArgs joins a, b and c and returns bytes like `a, b, c`.
+func formatAsCallArgs(pass *analysis.Pass, args ...ast.Expr) []byte {
+ if len(args) == 0 {
+ return []byte("")
+ }
+
+ var buf bytes.Buffer
+ for i, arg := range args {
+ buf.Write(analysisutil.NodeBytes(pass.Fset, arg))
+ if i != len(args)-1 {
+ buf.WriteString(", ")
+ }
+ }
+ return buf.Bytes()
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_http.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_http.go
new file mode 100644
index 000000000..9dabb02a9
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_http.go
@@ -0,0 +1,35 @@
+package checkers
+
+import (
+ "go/ast"
+ "go/types"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+func mimicHTTPHandler(pass *analysis.Pass, fType *ast.FuncType) bool {
+ httpHandlerFuncObj := analysisutil.ObjectOf(pass.Pkg, "net/http", "HandlerFunc")
+ if httpHandlerFuncObj == nil {
+ return false
+ }
+
+ sig, ok := httpHandlerFuncObj.Type().Underlying().(*types.Signature)
+ if !ok {
+ return false
+ }
+
+ if len(fType.Params.List) != sig.Params().Len() {
+ return false
+ }
+
+ for i := 0; i < sig.Params().Len(); i++ {
+ lhs := sig.Params().At(i).Type()
+ rhs := pass.TypesInfo.TypeOf(fType.Params.List[i].Type)
+ if !types.Identical(lhs, rhs) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go
new file mode 100644
index 000000000..b0c0d1302
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go
@@ -0,0 +1,48 @@
+package checkers
+
+import (
+ "go/ast"
+ "go/types"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+ "github.com/Antonboom/testifylint/internal/testify"
+)
+
+func isEmptyInterface(pass *analysis.Pass, expr ast.Expr) bool {
+ t, ok := pass.TypesInfo.Types[expr]
+ if !ok {
+ return false
+ }
+
+ iface, ok := t.Type.Underlying().(*types.Interface)
+ return ok && iface.NumMethods() == 0
+}
+
+func implementsTestifySuite(pass *analysis.Pass, e ast.Expr) bool {
+ suiteIfaceObj := analysisutil.ObjectOf(pass.Pkg, testify.SuitePkgPath, "TestingSuite")
+ return (suiteIfaceObj != nil) && implements(pass, e, suiteIfaceObj)
+}
+
+func implementsTestingT(pass *analysis.Pass, e ast.Expr) bool {
+ return implementsAssertTestingT(pass, e) || implementsRequireTestingT(pass, e)
+}
+
+func implementsAssertTestingT(pass *analysis.Pass, e ast.Expr) bool {
+ assertTestingTObj := analysisutil.ObjectOf(pass.Pkg, testify.AssertPkgPath, "TestingT")
+ return (assertTestingTObj != nil) && implements(pass, e, assertTestingTObj)
+}
+
+func implementsRequireTestingT(pass *analysis.Pass, e ast.Expr) bool {
+ requireTestingTObj := analysisutil.ObjectOf(pass.Pkg, testify.RequirePkgPath, "TestingT")
+ return (requireTestingTObj != nil) && implements(pass, e, requireTestingTObj)
+}
+
+func implements(pass *analysis.Pass, e ast.Expr, ifaceObj types.Object) bool {
+ t := pass.TypesInfo.TypeOf(e)
+ if t == nil {
+ return false
+ }
+ return types.Implements(t, ifaceObj.Type().Underlying().(*types.Interface))
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_len.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_len.go
new file mode 100644
index 000000000..904950ff3
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_len.go
@@ -0,0 +1,55 @@
+package checkers
+
+import (
+ "go/ast"
+ "go/token"
+ "go/types"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+var lenObj = types.Universe.Lookup("len")
+
+func isLenEquality(pass *analysis.Pass, e ast.Expr) (ast.Expr, ast.Expr, bool) {
+ be, ok := e.(*ast.BinaryExpr)
+ if !ok {
+ return nil, nil, false
+ }
+
+ if be.Op != token.EQL {
+ return nil, nil, false
+ }
+ return xorLenCall(pass, be.X, be.Y)
+}
+
+func xorLenCall(pass *analysis.Pass, a, b ast.Expr) (lenArg ast.Expr, expectedLen ast.Expr, ok bool) {
+ arg1, ok1 := isBuiltinLenCall(pass, a)
+ arg2, ok2 := isBuiltinLenCall(pass, b)
+
+ if xor(ok1, ok2) {
+ if ok1 {
+ return arg1, b, true
+ }
+ return arg2, a, true
+ }
+ return nil, nil, false
+}
+
+func isLenCallAndZero(pass *analysis.Pass, a, b ast.Expr) (ast.Expr, bool) {
+ lenArg, ok := isBuiltinLenCall(pass, a)
+ return lenArg, ok && isZero(b)
+}
+
+func isBuiltinLenCall(pass *analysis.Pass, e ast.Expr) (ast.Expr, bool) {
+ ce, ok := e.(*ast.CallExpr)
+ if !ok {
+ return nil, false
+ }
+
+ if analysisutil.IsObj(pass.TypesInfo, ce.Fun, lenObj) && len(ce.Args) == 1 {
+ return ce.Args[0], true
+ }
+ return nil, false
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_naming.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_naming.go
new file mode 100644
index 000000000..1d92e3e81
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_naming.go
@@ -0,0 +1,26 @@
+package checkers
+
+import (
+ "go/ast"
+ "regexp"
+)
+
+func isStructVarNamedAfterPattern(pattern *regexp.Regexp, e ast.Expr) bool {
+ s, ok := e.(*ast.SelectorExpr)
+ return ok && isIdentNamedAfterPattern(pattern, s.X)
+}
+
+func isStructFieldNamedAfterPattern(pattern *regexp.Regexp, e ast.Expr) bool {
+ s, ok := e.(*ast.SelectorExpr)
+ return ok && isIdentNamedAfterPattern(pattern, s.Sel)
+}
+
+func isIdentNamedAfterPattern(pattern *regexp.Regexp, e ast.Expr) bool {
+ id, ok := e.(*ast.Ident)
+ return ok && pattern.MatchString(id.Name)
+}
+
+func isIdentWithName(name string, e ast.Expr) bool {
+ id, ok := e.(*ast.Ident)
+ return ok && id.Name == name
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_nil.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_nil.go
new file mode 100644
index 000000000..112fca38e
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_nil.go
@@ -0,0 +1,18 @@
+package checkers
+
+import "go/ast"
+
+func xorNil(first, second ast.Expr) (ast.Expr, bool) {
+ a, b := isNil(first), isNil(second)
+ if xor(a, b) {
+ if a {
+ return second, true
+ }
+ return first, true
+ }
+ return nil, false
+}
+
+func isNil(expr ast.Expr) bool {
+ return isIdentWithName("nil", expr)
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_suite.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_suite.go
new file mode 100644
index 000000000..9f39d4653
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_suite.go
@@ -0,0 +1,40 @@
+package checkers
+
+import (
+ "go/ast"
+ "strings"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+func isSuiteMethod(pass *analysis.Pass, fDecl *ast.FuncDecl) bool {
+ if fDecl.Recv == nil || len(fDecl.Recv.List) != 1 {
+ return false
+ }
+
+ rcv := fDecl.Recv.List[0]
+ return implementsTestifySuite(pass, rcv.Type)
+}
+
+func isSuiteTestMethod(name string) bool {
+ return strings.HasPrefix(name, "Test")
+}
+
+func isSuiteServiceMethod(name string) bool {
+ // https://github.com/stretchr/testify/blob/master/suite/interfaces.go
+ switch name {
+ case "T", "SetT", "SetS", "SetupSuite", "SetupTest", "TearDownSuite", "TearDownTest",
+ "BeforeTest", "AfterTest", "HandleStats", "SetupSubTest", "TearDownSubTest":
+ return true
+ }
+ return false
+}
+
+func isSuiteAfterTestMethod(name string) bool {
+ // https://github.com/stretchr/testify/blob/master/suite/interfaces.go
+ switch name {
+ case "TearDownSuite", "TearDownTest", "AfterTest", "HandleStats", "TearDownSubTest":
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_testing.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_testing.go
new file mode 100644
index 000000000..5c28ec883
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_testing.go
@@ -0,0 +1,36 @@
+package checkers
+
+import (
+ "go/ast"
+
+ "golang.org/x/tools/go/analysis"
+)
+
+func isSubTestRun(pass *analysis.Pass, ce *ast.CallExpr) bool {
+ se, ok := ce.Fun.(*ast.SelectorExpr)
+ if !ok || se.Sel == nil {
+ return false
+ }
+ return (implementsTestingT(pass, se.X) || implementsTestifySuite(pass, se.X)) && se.Sel.Name == "Run"
+}
+
+func isTestingFuncOrMethod(pass *analysis.Pass, fd *ast.FuncDecl) bool {
+ return hasTestingTParam(pass, fd.Type) || isSuiteMethod(pass, fd)
+}
+
+func isTestingAnonymousFunc(pass *analysis.Pass, ft *ast.FuncType) bool {
+ return hasTestingTParam(pass, ft)
+}
+
+func hasTestingTParam(pass *analysis.Pass, ft *ast.FuncType) bool {
+ if ft == nil || ft.Params == nil {
+ return false
+ }
+
+ for _, param := range ft.Params.List {
+ if implementsTestingT(pass, param.Type) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/len.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/len.go
index d4e6a48b5..47330568c 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/len.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/len.go
@@ -1,9 +1,6 @@
package checkers
import (
- "go/ast"
- "go/token"
-
"golang.org/x/tools/go/analysis"
)
@@ -65,33 +62,3 @@ func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnost
}
return nil
}
-
-func xorLenCall(pass *analysis.Pass, a, b ast.Expr) (lenArg ast.Expr, expectedLen ast.Expr, ok bool) {
- arg1, ok1 := isBuiltinLenCall(pass, a)
- arg2, ok2 := isBuiltinLenCall(pass, b)
-
- if xor(ok1, ok2) {
- if ok1 {
- return arg1, b, true
- }
- return arg2, a, true
- }
- return nil, nil, false
-}
-
-func isLenEquality(pass *analysis.Pass, e ast.Expr) (ast.Expr, ast.Expr, bool) {
- be, ok := e.(*ast.BinaryExpr)
- if !ok {
- return nil, nil, false
- }
-
- if be.Op != token.EQL {
- return nil, nil, false
- }
- return xorLenCall(pass, be.X, be.Y)
-}
-
-func isIntBasicLit(e ast.Expr) bool {
- bl, ok := e.(*ast.BasicLit)
- return ok && bl.Kind == token.INT
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go
new file mode 100644
index 000000000..274021f67
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go
@@ -0,0 +1,175 @@
+package checkers
+
+import (
+ "go/ast"
+ "go/token"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+// NegativePositive detects situations like
+//
+// assert.Less(t, a, 0)
+// assert.Greater(t, 0, a)
+// assert.True(t, a < 0)
+// assert.True(t, 0 > a)
+// assert.False(t, a >= 0)
+// assert.False(t, 0 <= a)
+//
+// assert.Greater(t, a, 0)
+// assert.Less(t, 0, a)
+// assert.True(t, a > 0)
+// assert.True(t, 0 < a)
+// assert.False(t, a <= 0)
+// assert.False(t, 0 >= a)
+//
+// and requires
+//
+// assert.Negative(t, value)
+// assert.Positive(t, value)
+//
+// Typed zeros (like `int8(0)`, ..., `uint64(0)`) are also supported.
+type NegativePositive struct{}
+
+// NewNegativePositive constructs NegativePositive checker.
+func NewNegativePositive() NegativePositive { return NegativePositive{} }
+func (NegativePositive) Name() string { return "negative-positive" }
+
+func (checker NegativePositive) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic {
+ if d := checker.checkNegative(pass, call); d != nil {
+ return d
+ }
+ return checker.checkPositive(pass, call)
+}
+
+func (checker NegativePositive) checkNegative(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic {
+ newUseNegativeDiagnostic := func(replaceStart, replaceEnd token.Pos, replaceWith ast.Expr) *analysis.Diagnostic {
+ const proposed = "Negative"
+ return newUseFunctionDiagnostic(checker.Name(), call, proposed,
+ newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
+ Pos: replaceStart,
+ End: replaceEnd,
+ NewText: analysisutil.NodeBytes(pass.Fset, replaceWith),
+ }),
+ )
+ }
+
+ // NOTE(a.telyshev): We ignore uint-asserts as being no sense for assert.Negative.
+
+ switch call.Fn.NameFTrimmed {
+ case "Less":
+ if len(call.Args) < 2 {
+ return nil
+ }
+ a, b := call.Args[0], call.Args[1]
+
+ if isSignedNotZero(pass, a) && isZeroOrSignedZero(b) {
+ return newUseNegativeDiagnostic(a.Pos(), b.End(), untype(a))
+ }
+
+ case "Greater":
+ if len(call.Args) < 2 {
+ return nil
+ }
+ a, b := call.Args[0], call.Args[1]
+
+ if isZeroOrSignedZero(a) && isSignedNotZero(pass, b) {
+ return newUseNegativeDiagnostic(a.Pos(), b.End(), untype(b))
+ }
+
+ case "True":
+ if len(call.Args) < 1 {
+ return nil
+ }
+ expr := call.Args[0]
+
+ a, _, ok1 := isStrictComparisonWith(pass, expr, isSignedNotZero, token.LSS, p(isZeroOrSignedZero)) // a < 0
+ _, b, ok2 := isStrictComparisonWith(pass, expr, p(isZeroOrSignedZero), token.GTR, isSignedNotZero) // 0 > a
+
+ survivingArg, ok := anyVal([]bool{ok1, ok2}, a, b)
+ if ok {
+ return newUseNegativeDiagnostic(expr.Pos(), expr.End(), untype(survivingArg))
+ }
+
+ case "False":
+ if len(call.Args) < 1 {
+ return nil
+ }
+ expr := call.Args[0]
+
+ a, _, ok1 := isStrictComparisonWith(pass, expr, isSignedNotZero, token.GEQ, p(isZeroOrSignedZero)) // a >= 0
+ _, b, ok2 := isStrictComparisonWith(pass, expr, p(isZeroOrSignedZero), token.LEQ, isSignedNotZero) // 0 <= a
+
+ survivingArg, ok := anyVal([]bool{ok1, ok2}, a, b)
+ if ok {
+ return newUseNegativeDiagnostic(expr.Pos(), expr.End(), untype(survivingArg))
+ }
+ }
+ return nil
+}
+
+func (checker NegativePositive) checkPositive(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic {
+ newUsePositiveDiagnostic := func(replaceStart, replaceEnd token.Pos, replaceWith ast.Expr) *analysis.Diagnostic {
+ const proposed = "Positive"
+ return newUseFunctionDiagnostic(checker.Name(), call, proposed,
+ newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{
+ Pos: replaceStart,
+ End: replaceEnd,
+ NewText: analysisutil.NodeBytes(pass.Fset, replaceWith),
+ }),
+ )
+ }
+
+ switch call.Fn.NameFTrimmed {
+ case "Greater":
+ if len(call.Args) < 2 {
+ return nil
+ }
+ a, b := call.Args[0], call.Args[1]
+
+ if isNotAnyZero(a) && isAnyZero(b) {
+ return newUsePositiveDiagnostic(a.Pos(), b.End(), untype(a))
+ }
+
+ case "Less":
+ if len(call.Args) < 2 {
+ return nil
+ }
+ a, b := call.Args[0], call.Args[1]
+
+ if isAnyZero(a) && isNotAnyZero(b) {
+ return newUsePositiveDiagnostic(a.Pos(), b.End(), untype(b))
+ }
+
+ case "True":
+ if len(call.Args) < 1 {
+ return nil
+ }
+ expr := call.Args[0]
+
+ a, _, ok1 := isStrictComparisonWith(pass, expr, p(isNotAnyZero), token.GTR, p(isAnyZero)) // a > 0
+ _, b, ok2 := isStrictComparisonWith(pass, expr, p(isAnyZero), token.LSS, p(isNotAnyZero)) // 0 < a
+
+ survivingArg, ok := anyVal([]bool{ok1, ok2}, a, b)
+ if ok {
+ return newUsePositiveDiagnostic(expr.Pos(), expr.End(), untype(survivingArg))
+ }
+
+ case "False":
+ if len(call.Args) < 1 {
+ return nil
+ }
+ expr := call.Args[0]
+
+ a, _, ok1 := isStrictComparisonWith(pass, expr, p(isNotAnyZero), token.LEQ, p(isAnyZero)) // a <= 0
+ _, b, ok2 := isStrictComparisonWith(pass, expr, p(isAnyZero), token.GEQ, p(isNotAnyZero)) // 0 >= a
+
+ survivingArg, ok := anyVal([]bool{ok1, ok2}, a, b)
+ if ok {
+ return newUsePositiveDiagnostic(expr.Pos(), expr.End(), untype(survivingArg))
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/nil_compare.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/nil_compare.go
index 89680a069..47c4a7383 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/nil_compare.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/nil_compare.go
@@ -1,8 +1,6 @@
package checkers
import (
- "go/ast"
-
"golang.org/x/tools/go/analysis"
"github.com/Antonboom/testifylint/internal/analysisutil"
@@ -56,14 +54,3 @@ func (checker NilCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.D
}),
)
}
-
-func xorNil(first, second ast.Expr) (ast.Expr, bool) {
- a, b := isNil(first), isNil(second)
- if xor(a, b) {
- if a {
- return second, true
- }
- return first, true
- }
- return nil, false
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/LICENSE b/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/LICENSE
new file mode 100644
index 000000000..6a66aea5e
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/doc.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/doc.go
new file mode 100644
index 000000000..09cd23993
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/doc.go
@@ -0,0 +1,6 @@
+// Package printf is a patched fork of
+// https://github.com/golang/tools/blob/b6235391adb3b7f8bcfc4df81055e8f023de2688/go/analysis/passes/printf/printf.go#L538
+//
+// Initial discussion:
+// https://go-review.googlesource.com/c/tools/+/580555/comments/dfe3ef96_b1b815d5
+package printf
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go
new file mode 100644
index 000000000..cfb47b542
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go
@@ -0,0 +1,559 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package printf
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+
+ "golang.org/x/tools/go/analysis"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+// CheckPrintf checks a call to a formatted print routine such as Printf.
+func CheckPrintf(
+ pass *analysis.Pass,
+ call *ast.CallExpr,
+ fnName string,
+ format string,
+ formatIdx int,
+) {
+ firstArg := formatIdx + 1 // Arguments are immediately after format string.
+ if !strings.Contains(format, "%") {
+ if len(call.Args) > firstArg {
+ pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", fnName)
+ }
+ return
+ }
+ // Hard part: check formats against args.
+ argNum := firstArg
+ maxArgNum := firstArg
+ anyIndex := false
+ for i, w := 0, 0; i < len(format); i += w {
+ w = 1
+ if format[i] != '%' {
+ continue
+ }
+ state := parsePrintfVerb(pass, call, fnName, format[i:], firstArg, argNum)
+ if state == nil {
+ return
+ }
+ w = len(state.format)
+ if !okPrintfArg(pass, call, state) { // One error per format is enough.
+ return
+ }
+ if state.hasIndex {
+ anyIndex = true
+ }
+ if state.verb == 'w' {
+ pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", state.name)
+ return
+ }
+ if len(state.argNums) > 0 {
+ // Continue with the next sequential argument.
+ argNum = state.argNums[len(state.argNums)-1] + 1
+ }
+ for _, n := range state.argNums {
+ if n >= maxArgNum {
+ maxArgNum = n + 1
+ }
+ }
+ }
+ // Dotdotdot is hard.
+ if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 {
+ return
+ }
+ // If any formats are indexed, extra arguments are ignored.
+ if anyIndex {
+ return
+ }
+ // There should be no leftover arguments.
+ if maxArgNum != len(call.Args) {
+ expect := maxArgNum - firstArg
+ numArgs := len(call.Args) - firstArg
+ pass.ReportRangef(call, "%s call needs %v but has %v", fnName, count(expect, "arg"), count(numArgs, "arg"))
+ }
+}
+
+// formatState holds the parsed representation of a printf directive such as "%3.*[4]d".
+// It is constructed by parsePrintfVerb.
+type formatState struct {
+ verb rune // the format verb: 'd' for "%d"
+ format string // the full format directive from % through verb, "%.3d".
+ name string // Printf, Sprintf etc.
+ flags []byte // the list of # + etc.
+ argNums []int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call
+ firstArg int // Index of first argument after the format in the Printf call.
+ // Used only during parse.
+ pass *analysis.Pass
+ call *ast.CallExpr
+ argNum int // Which argument we're expecting to format now.
+ hasIndex bool // Whether the argument is indexed.
+ indexPending bool // Whether we have an indexed argument that has not resolved.
+ nbytes int // number of bytes of the format string consumed.
+}
+
+// parseFlags accepts any printf flags.
+func (s *formatState) parseFlags() {
+ for s.nbytes < len(s.format) {
+ switch c := s.format[s.nbytes]; c {
+ case '#', '0', '+', '-', ' ':
+ s.flags = append(s.flags, c)
+ s.nbytes++
+ default:
+ return
+ }
+ }
+}
+
+// scanNum advances through a decimal number if present.
+func (s *formatState) scanNum() {
+ for ; s.nbytes < len(s.format); s.nbytes++ {
+ c := s.format[s.nbytes]
+ if c < '0' || '9' < c {
+ return
+ }
+ }
+}
+
+// parseIndex scans an index expression. It returns false if there is a syntax error.
+func (s *formatState) parseIndex() bool {
+ if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' {
+ return true
+ }
+ // Argument index present.
+ s.nbytes++ // skip '['
+ start := s.nbytes
+ s.scanNum()
+ ok := true
+ if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' {
+ ok = false // syntax error is either missing "]" or invalid index.
+ s.nbytes = strings.Index(s.format[start:], "]")
+ if s.nbytes < 0 {
+ s.pass.ReportRangef(s.call, "%s format %s is missing closing ]", s.name, s.format)
+ return false
+ }
+ s.nbytes += start
+ }
+ arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
+ if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) {
+ s.pass.ReportRangef(s.call, "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes])
+ return false
+ }
+ s.nbytes++ // skip ']'
+ arg := int(arg32)
+ arg += s.firstArg - 1 // We want to zero-index the actual arguments.
+ s.argNum = arg
+ s.hasIndex = true
+ s.indexPending = true
+ return true
+}
+
+// parseNum scans a width or precision (or *). It returns false if there's a bad index expression.
+func (s *formatState) parseNum() bool {
+ if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' {
+ if s.indexPending { // Absorb it.
+ s.indexPending = false
+ }
+ s.nbytes++
+ s.argNums = append(s.argNums, s.argNum)
+ s.argNum++
+ } else {
+ s.scanNum()
+ }
+ return true
+}
+
+// parsePrecision scans for a precision. It returns false if there's a bad index expression.
+func (s *formatState) parsePrecision() bool {
+ // If there's a period, there may be a precision.
+ if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' {
+ s.flags = append(s.flags, '.') // Treat precision as a flag.
+ s.nbytes++
+ if !s.parseIndex() {
+ return false
+ }
+ if !s.parseNum() {
+ return false
+ }
+ }
+ return true
+}
+
+// isFormatter reports whether t could satisfy fmt.Formatter.
+// The only interface method to look for is "Format(State, rune)".
+func isFormatter(typ types.Type) bool {
+ // If the type is an interface, the value it holds might satisfy fmt.Formatter.
+ if _, ok := typ.Underlying().(*types.Interface); ok {
+ // Don't assume type parameters could be formatters. With the greater
+ // expressiveness of constraint interface syntax we expect more type safety
+ // when using type parameters.
+ if !isTypeParam(typ) {
+ return true
+ }
+ }
+ obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format")
+ fn, ok := obj.(*types.Func)
+ if !ok {
+ return false
+ }
+ sig := fn.Type().(*types.Signature)
+ return sig.Params().Len() == 2 &&
+ sig.Results().Len() == 0 &&
+ isNamedType(sig.Params().At(0).Type(), "fmt", "State") &&
+ types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune])
+}
+
+// isTypeParam reports whether t is a type parameter.
+func isTypeParam(t types.Type) bool {
+ _, ok := t.(*types.TypeParam)
+ return ok
+}
+
+// isNamedType reports whether t is the named type with the given package path
+// and one of the given names.
+// This function avoids allocating the concatenation of "pkg.Name",
+// which is important for the performance of syntax matching.
+func isNamedType(t types.Type, pkgPath string, names ...string) bool {
+ n, ok := t.(*types.Named)
+ if !ok {
+ return false
+ }
+ obj := n.Obj()
+ if obj == nil || obj.Pkg() == nil || obj.Pkg().Path() != pkgPath {
+ return false
+ }
+ name := obj.Name()
+ for _, n := range names {
+ if name == n {
+ return true
+ }
+ }
+ return false
+}
+
+// parsePrintfVerb looks the formatting directive that begins the format string
+// and returns a formatState that encodes what the directive wants, without looking
+// at the actual arguments present in the call. The result is nil if there is an error.
+func parsePrintfVerb(pass *analysis.Pass, call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState {
+ state := &formatState{
+ format: format,
+ name: name,
+ flags: make([]byte, 0, 5),
+ argNum: argNum,
+ argNums: make([]int, 0, 1),
+ nbytes: 1, // There's guaranteed to be a percent sign.
+ firstArg: firstArg,
+ pass: pass,
+ call: call,
+ }
+ // There may be flags.
+ state.parseFlags()
+ // There may be an index.
+ if !state.parseIndex() {
+ return nil
+ }
+ // There may be a width.
+ if !state.parseNum() {
+ return nil
+ }
+ // There may be a precision.
+ if !state.parsePrecision() {
+ return nil
+ }
+ // Now a verb, possibly prefixed by an index (which we may already have).
+ if !state.indexPending && !state.parseIndex() {
+ return nil
+ }
+ if state.nbytes == len(state.format) {
+ pass.ReportRangef(call.Fun, "%s format %s is missing verb at end of string", name, state.format)
+ return nil
+ }
+ verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:])
+ state.verb = verb
+ state.nbytes += w
+ if verb != '%' {
+ state.argNums = append(state.argNums, state.argNum)
+ }
+ state.format = state.format[:state.nbytes]
+ return state
+}
+
+// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
+type printfArgType int
+
+const (
+ argBool printfArgType = 1 << iota
+ argInt
+ argRune
+ argString
+ argFloat
+ argComplex
+ argPointer
+ argError
+ anyType printfArgType = ^0
+)
+
+type printVerb struct {
+ verb rune // User may provide verb through Formatter; could be a rune.
+ flags string // known flags are all ASCII
+ typ printfArgType
+}
+
+// Common flag sets for printf verbs.
+const (
+ noFlag = ""
+ numFlag = " -+.0"
+ sharpNumFlag = " -+.0#"
+ allFlags = " -+.0#"
+)
+
+// printVerbs identifies which flags are known to printf for each verb.
+var printVerbs = []printVerb{
+ // '-' is a width modifier, always valid.
+ // '.' is a precision for float, max width for strings.
+ // '+' is required sign for numbers, Go format for %v.
+ // '#' is alternate format for several verbs.
+ // ' ' is spacer for numbers
+ {'%', noFlag, 0},
+ {'b', sharpNumFlag, argInt | argFloat | argComplex | argPointer},
+ {'c', "-", argRune | argInt},
+ {'d', numFlag, argInt | argPointer},
+ {'e', sharpNumFlag, argFloat | argComplex},
+ {'E', sharpNumFlag, argFloat | argComplex},
+ {'f', sharpNumFlag, argFloat | argComplex},
+ {'F', sharpNumFlag, argFloat | argComplex},
+ {'g', sharpNumFlag, argFloat | argComplex},
+ {'G', sharpNumFlag, argFloat | argComplex},
+ {'o', sharpNumFlag, argInt | argPointer},
+ {'O', sharpNumFlag, argInt | argPointer},
+ {'p', "-#", argPointer},
+ {'q', " -+.0#", argRune | argInt | argString},
+ {'s', " -+.0", argString},
+ {'t', "-", argBool},
+ {'T', "-", anyType},
+ {'U', "-#", argRune | argInt},
+ {'v', allFlags, anyType},
+ {'w', allFlags, argError},
+ {'x', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
+ {'X', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
+}
+
+// okPrintfArg compares the formatState to the arguments actually present,
+// reporting any discrepancies it can discern. If the final argument is ellipsissed,
+// there's little it can do for that.
+func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (ok bool) {
+ var v printVerb
+ found := false
+ // Linear scan is fast enough for a small list.
+ for _, v = range printVerbs {
+ if v.verb == state.verb {
+ found = true
+ break
+ }
+ }
+
+ // Could current arg implement fmt.Formatter?
+ // Skip check for the %w verb, which requires an error.
+ formatter := false
+ if v.typ != argError && state.argNum < len(call.Args) {
+ if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok {
+ formatter = isFormatter(tv.Type)
+ }
+ }
+
+ if !formatter {
+ if !found {
+ pass.ReportRangef(call, "%s format %s has unknown verb %c", state.name, state.format, state.verb)
+ return false
+ }
+ for _, flag := range state.flags {
+ // TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11.
+ // See issues 23598 and 23605.
+ if flag == '0' {
+ continue
+ }
+ if !strings.ContainsRune(v.flags, rune(flag)) {
+ pass.ReportRangef(call, "%s format %s has unrecognized flag %c", state.name, state.format, flag)
+ return false
+ }
+ }
+ }
+ // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all
+ // but the final arg must be an integer.
+ trueArgs := 1
+ if state.verb == '%' {
+ trueArgs = 0
+ }
+ nargs := len(state.argNums)
+ for i := 0; i < nargs-trueArgs; i++ {
+ if !argCanBeChecked(pass, call, i, state) {
+ return
+ }
+ // NOTE(a.telyshev): `matchArgType` leads to a lot of "golang.org/x/tools/internal" code.
+ /*
+ argNum := state.argNums[i]
+ arg := call.Args[argNum]
+
+ if reason, ok := matchArgType(pass, argInt, arg); !ok {
+ details := ""
+ if reason != "" {
+ details = " (" + reason + ")"
+ }
+ pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg), details)
+ return false
+ }
+ */
+ }
+
+ if state.verb == '%' || formatter {
+ return true
+ }
+ argNum := state.argNums[len(state.argNums)-1]
+ if !argCanBeChecked(pass, call, len(state.argNums)-1, state) {
+ return false
+ }
+ arg := call.Args[argNum]
+ if isFunctionValue(pass, arg) && state.verb != 'p' && state.verb != 'T' {
+ pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", state.name, state.format, analysisutil.NodeString(pass.Fset, arg))
+ return false
+ }
+ // NOTE(a.telyshev): `matchArgType` leads to a lot of "golang.org/x/tools/internal" code.
+ /*
+ if reason, ok := matchArgType(pass, v.typ, arg); !ok {
+ typeString := ""
+ if typ := pass.TypesInfo.Types[arg].Type; typ != nil {
+ typeString = typ.String()
+ }
+ details := ""
+ if reason != "" {
+ details = " (" + reason + ")"
+ }
+ pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString, details)
+ return false
+ }
+ */
+ if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) {
+ if methodName, ok := recursiveStringer(pass, arg); ok {
+ pass.ReportRangef(call, "%s format %s with arg %s causes recursive %s method call", state.name, state.format, analysisutil.NodeString(pass.Fset, arg), methodName)
+ return false
+ }
+ }
+ return true
+}
+
+// recursiveStringer reports whether the argument e is a potential
+// recursive call to stringer or is an error, such as t and &t in these examples:
+//
+// func (t *T) String() string { printf("%s", t) }
+// func (t T) Error() string { printf("%s", t) }
+// func (t T) String() string { printf("%s", &t) }
+func recursiveStringer(pass *analysis.Pass, e ast.Expr) (string, bool) {
+ typ := pass.TypesInfo.Types[e].Type
+
+ // It's unlikely to be a recursive stringer if it has a Format method.
+ if isFormatter(typ) {
+ return "", false
+ }
+
+ // Does e allow e.String() or e.Error()?
+ strObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "String")
+ strMethod, strOk := strObj.(*types.Func)
+ errObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "Error")
+ errMethod, errOk := errObj.(*types.Func)
+ if !strOk && !errOk {
+ return "", false
+ }
+
+ // inScope returns true if e is in the scope of f.
+ inScope := func(e ast.Expr, f *types.Func) bool {
+ return f.Scope() != nil && f.Scope().Contains(e.Pos())
+ }
+
+ // Is the expression e within the body of that String or Error method?
+ var method *types.Func
+ if strOk && strMethod.Pkg() == pass.Pkg && inScope(e, strMethod) {
+ method = strMethod
+ } else if errOk && errMethod.Pkg() == pass.Pkg && inScope(e, errMethod) {
+ method = errMethod
+ } else {
+ return "", false
+ }
+
+ sig := method.Type().(*types.Signature)
+ if !isStringer(sig) {
+ return "", false
+ }
+
+ // Is it the receiver r, or &r?
+ if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND {
+ e = u.X // strip off & from &r
+ }
+ if id, ok := e.(*ast.Ident); ok {
+ if pass.TypesInfo.Uses[id] == sig.Recv() {
+ return method.FullName(), true
+ }
+ }
+ return "", false
+}
+
+// isStringer reports whether the method signature matches the String() definition in fmt.Stringer.
+func isStringer(sig *types.Signature) bool {
+ return sig.Params().Len() == 0 &&
+ sig.Results().Len() == 1 &&
+ sig.Results().At(0).Type() == types.Typ[types.String]
+}
+
+// isFunctionValue reports whether the expression is a function as opposed to a function call.
+// It is almost always a mistake to print a function value.
+func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
+ if typ := pass.TypesInfo.Types[e].Type; typ != nil {
+ // Don't call Underlying: a named func type with a String method is ok.
+ // TODO(adonovan): it would be more precise to check isStringer.
+ _, ok := typ.(*types.Signature)
+ return ok
+ }
+ return false
+}
+
+// argCanBeChecked reports whether the specified argument is statically present;
+// it may be beyond the list of arguments or in a terminal slice... argument, which
+// means we can't see it.
+func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, formatArg int, state *formatState) bool {
+ argNum := state.argNums[formatArg]
+ if argNum <= 0 {
+ return false
+ }
+ if argNum < len(call.Args)-1 {
+ return true // Always OK.
+ }
+ if call.Ellipsis.IsValid() {
+ return false // We just can't tell; there could be many more arguments.
+ }
+ if argNum < len(call.Args) {
+ return true
+ }
+ // There are bad indexes in the format or there are fewer arguments than the format needs.
+ // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
+ arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed.
+ pass.ReportRangef(call, "%s format %s reads arg #%d, but call has %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg"))
+ return false
+}
+
+// count(n, what) returns "1 what" or "N whats"
+// (assuming the plural of what is whats).
+func count(n int, what string) string {
+ if n == 1 {
+ return "1 " + what
+ }
+ return fmt.Sprintf("%d %ss", n, what)
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/require_error.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/require_error.go
index ab09dd447..4303828fd 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/require_error.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/require_error.go
@@ -1,9 +1,7 @@
package checkers
import (
- "fmt"
"go/ast"
- "go/token"
"regexp"
"golang.org/x/tools/go/analysis"
@@ -30,10 +28,11 @@ const requireErrorReport = "for error assertions use require"
// ...
//
// RequireError ignores:
-// - assertion in the `if` condition;
+// - assertions in the `if` condition;
+// - assertions in the bool expression;
// - the entire `if-else[-if]` block, if there is an assertion in any `if` condition;
// - the last assertion in the block, if there are no methods/functions calls after it;
-// - assertions in an explicit goroutine;
+// - assertions in an explicit goroutine (including `http.Handler`);
// - assertions in an explicit testing cleanup function or suite teardown methods;
// - sequence of NoError assertions.
type RequireError struct {
@@ -74,6 +73,8 @@ func (checker RequireError) Check(pass *analysis.Pass, inspector *inspector.Insp
_, prevPrevIsIfStmt := stack[len(stack)-3].(*ast.IfStmt)
inIfCond := prevIsIfStmt || (prevPrevIsIfStmt && prevIsAssignStmt)
+ _, inBoolExpr := stack[len(stack)-2].(*ast.BinaryExpr)
+
callExpr := node.(*ast.CallExpr)
testifyCall := NewCallMeta(pass, callExpr)
@@ -84,6 +85,7 @@ func (checker RequireError) Check(pass *analysis.Pass, inspector *inspector.Insp
parentIf: findNearestNode[*ast.IfStmt](stack),
parentBlock: findNearestNode[*ast.BlockStmt](stack),
inIfCond: inIfCond,
+ inBoolExpr: inBoolExpr,
inNoErrorSeq: false, // Will be filled in below.
}
@@ -108,10 +110,7 @@ func (checker RequireError) Check(pass *analysis.Pass, inspector *inspector.Insp
for funcInfo, calls := range callsByFunc {
for i, c := range calls {
- if funcInfo.isTestCleanup {
- continue
- }
- if funcInfo.isGoroutine {
+ if m := funcInfo.meta; m.isTestCleanup || m.isGoroutine || m.isHTTPHandler {
continue
}
@@ -148,13 +147,7 @@ func needToSkipBasedOnContext(
otherCalls []*callMeta,
callsByBlock map[*ast.BlockStmt][]*callMeta,
) bool {
- if currCall.inNoErrorSeq {
- // Skip `assert.NoError` sequence.
- return true
- }
-
- if currCall.inIfCond {
- // Skip assertions in the "if condition".
+ if currCall.inIfCond || currCall.inBoolExpr || currCall.inNoErrorSeq {
return true
}
@@ -200,53 +193,6 @@ func needToSkipBasedOnContext(
return isLastCallInBlock && noCallsAfter
}
-func findSurroundingFunc(pass *analysis.Pass, stack []ast.Node) *funcID {
- for i := len(stack) - 2; i >= 0; i-- {
- var fType *ast.FuncType
- var fName string
- var isTestCleanup bool
- var isGoroutine bool
-
- switch fd := stack[i].(type) {
- case *ast.FuncDecl:
- fType, fName = fd.Type, fd.Name.Name
-
- if isTestifySuiteMethod(pass, fd) {
- if ident := fd.Name; ident != nil && isAfterTestMethod(ident.Name) {
- isTestCleanup = true
- }
- }
-
- case *ast.FuncLit:
- fType, fName = fd.Type, "anonymous"
-
- if i >= 2 { //nolint:nestif
- if ce, ok := stack[i-1].(*ast.CallExpr); ok {
- if se, ok := ce.Fun.(*ast.SelectorExpr); ok {
- isTestCleanup = isTestingTPtr(pass, se.X) && se.Sel != nil && (se.Sel.Name == "Cleanup")
- }
-
- if _, ok := stack[i-2].(*ast.GoStmt); ok {
- isGoroutine = true
- }
- }
- }
-
- default:
- continue
- }
-
- return &funcID{
- pos: fType.Pos(),
- posStr: pass.Fset.Position(fType.Pos()).String(),
- name: fName,
- isTestCleanup: isTestCleanup,
- isGoroutine: isGoroutine,
- }
- }
- return nil
-}
-
func findRootIf(stack []ast.Node) *ast.IfStmt {
nearestIf, i := findNearestNodeWithIdx[*ast.IfStmt](stack)
for ; i > 0; i-- {
@@ -260,20 +206,6 @@ func findRootIf(stack []ast.Node) *ast.IfStmt {
return nearestIf
}
-func findNearestNode[T ast.Node](stack []ast.Node) (v T) {
- v, _ = findNearestNodeWithIdx[T](stack)
- return
-}
-
-func findNearestNodeWithIdx[T ast.Node](stack []ast.Node) (v T, index int) {
- for i := len(stack) - 2; i >= 0; i-- {
- if n, ok := stack[i].(T); ok {
- return n, i
- }
- }
- return
-}
-
func markCallsInNoErrorSequence(callsByBlock map[*ast.BlockStmt][]*callMeta) {
for _, calls := range callsByBlock {
for i, c := range calls {
@@ -309,30 +241,10 @@ type callMeta struct {
parentIf *ast.IfStmt // The nearest `if`, can be equal with rootIf.
parentBlock *ast.BlockStmt
inIfCond bool // True for code like `if assert.ErrorAs(t, err, &target) {`.
+ inBoolExpr bool // True for code like `assert.Error(t, err) && assert.ErrorContains(t, err, "value")`
inNoErrorSeq bool // True for sequence of `assert.NoError` assertions.
}
-type funcID struct {
- pos token.Pos
- posStr string
- name string
- isTestCleanup bool
- isGoroutine bool
-}
-
-func (id funcID) String() string {
- return fmt.Sprintf("%s at %s", id.name, id.posStr)
-}
-
-func isAfterTestMethod(name string) bool {
- // https://github.com/stretchr/testify/blob/master/suite/interfaces.go
- switch name {
- case "TearDownSuite", "TearDownTest", "AfterTest", "HandleStats", "TearDownSubTest":
- return true
- }
- return false
-}
-
func isNoErrorAssertion(fnName string) bool {
return (fnName == "NoError") || (fnName == "NoErrorf")
}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_broken_parallel.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_broken_parallel.go
new file mode 100644
index 000000000..f830fd2a5
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_broken_parallel.go
@@ -0,0 +1,89 @@
+package checkers
+
+import (
+ "fmt"
+ "go/ast"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/ast/inspector"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+// SuiteBrokenParallel detects unsupported t.Parallel() call in suite tests
+//
+// func (s *MySuite) SetupTest() {
+// s.T().Parallel()
+// }
+//
+// // And other hooks...
+//
+// func (s *MySuite) TestSomething() {
+// s.T().Parallel()
+//
+// for _, tt := range cases {
+// s.Run(tt.name, func() {
+// s.T().Parallel()
+// })
+//
+// s.T().Run(tt.name, func(t *testing.T) {
+// t.Parallel()
+// })
+// }
+// }
+type SuiteBrokenParallel struct{}
+
+// NewSuiteBrokenParallel constructs SuiteBrokenParallel checker.
+func NewSuiteBrokenParallel() SuiteBrokenParallel { return SuiteBrokenParallel{} }
+func (SuiteBrokenParallel) Name() string { return "suite-broken-parallel" }
+
+func (checker SuiteBrokenParallel) Check(pass *analysis.Pass, insp *inspector.Inspector) (diagnostics []analysis.Diagnostic) {
+ const report = "testify v1 does not support suite's parallel tests and subtests"
+
+ insp.WithStack([]ast.Node{(*ast.CallExpr)(nil)}, func(node ast.Node, push bool, stack []ast.Node) bool {
+ if !push {
+ return false
+ }
+ ce := node.(*ast.CallExpr)
+
+ se, ok := ce.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return true
+ }
+ if !isIdentWithName("Parallel", se.Sel) {
+ return true
+ }
+ if !implementsTestingT(pass, se.X) {
+ return true
+ }
+
+ for i := len(stack) - 2; i >= 0; i-- {
+ fd, ok := stack[i].(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+
+ if !isSuiteMethod(pass, fd) {
+ continue
+ }
+
+ nextLine := pass.Fset.Position(ce.Pos()).Line + 1
+ d := newDiagnostic(checker.Name(), ce, report, &analysis.SuggestedFix{
+ Message: fmt.Sprintf("Remove `%s` call", analysisutil.NodeString(pass.Fset, ce)),
+ TextEdits: []analysis.TextEdit{
+ {
+ Pos: ce.Pos(),
+ End: pass.Fset.File(ce.Pos()).LineStart(nextLine),
+ NewText: []byte(""),
+ },
+ },
+ })
+
+ diagnostics = append(diagnostics, *d)
+ return false
+ }
+
+ return true
+ })
+ return diagnostics
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_dont_use_pkg.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_dont_use_pkg.go
index bf84f6378..6150ae78d 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_dont_use_pkg.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_dont_use_pkg.go
@@ -3,15 +3,11 @@ package checkers
import (
"fmt"
"go/ast"
- "go/types"
"golang.org/x/tools/go/analysis"
-
- "github.com/Antonboom/testifylint/internal/analysisutil"
- "github.com/Antonboom/testifylint/internal/testify"
)
-// SuiteDontUsePkg detects situation like
+// SuiteDontUsePkg detects situations like
//
// func (s *MySuite) TestSomething() {
// assert.Equal(s.T(), 42, value)
@@ -47,7 +43,7 @@ func (checker SuiteDontUsePkg) Check(pass *analysis.Pass, call *CallMeta) *analy
if !ok {
return nil
}
- if se.X == nil || !implementsTestifySuiteIface(pass, se.X) {
+ if se.X == nil || !implementsTestifySuite(pass, se.X) {
return nil
}
if se.Sel == nil || se.Sel.Name != "T" {
@@ -82,15 +78,3 @@ func (checker SuiteDontUsePkg) Check(pass *analysis.Pass, call *CallMeta) *analy
},
})
}
-
-func implementsTestifySuiteIface(pass *analysis.Pass, rcv ast.Expr) bool {
- suiteIface := analysisutil.ObjectOf(pass.Pkg, testify.SuitePkgPath, "TestingSuite")
- if suiteIface == nil {
- return false
- }
-
- return types.Implements(
- pass.TypesInfo.TypeOf(rcv),
- suiteIface.Type().Underlying().(*types.Interface),
- )
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_extra_assert_call.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_extra_assert_call.go
index 791488b65..9adfe5190 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_extra_assert_call.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_extra_assert_call.go
@@ -19,7 +19,7 @@ const (
const DefaultSuiteExtraAssertCallMode = SuiteExtraAssertCallModeRemove
-// SuiteExtraAssertCall detects situation like
+// SuiteExtraAssertCall detects situations like
//
// func (s *MySuite) TestSomething() {
// s.Assert().Equal(42, value)
@@ -56,7 +56,7 @@ func (checker SuiteExtraAssertCall) Check(pass *analysis.Pass, call *CallMeta) *
switch checker.mode {
case SuiteExtraAssertCallModeRequire:
x, ok := call.Selector.X.(*ast.Ident) // s.True
- if !ok || x == nil || !implementsTestifySuiteIface(pass, x) {
+ if !ok || x == nil || !implementsTestifySuite(pass, x) {
return nil
}
@@ -77,7 +77,7 @@ func (checker SuiteExtraAssertCall) Check(pass *analysis.Pass, call *CallMeta) *
}
se, ok := x.Fun.(*ast.SelectorExpr)
- if !ok || se == nil || !implementsTestifySuiteIface(pass, se.X) {
+ if !ok || se == nil || !implementsTestifySuite(pass, se.X) {
return nil
}
if se.Sel == nil || se.Sel.Name != "Assert" {
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_subtest_run.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_subtest_run.go
new file mode 100644
index 000000000..67d9c252b
--- /dev/null
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_subtest_run.go
@@ -0,0 +1,60 @@
+package checkers
+
+import (
+ "fmt"
+ "go/ast"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/ast/inspector"
+
+ "github.com/Antonboom/testifylint/internal/analysisutil"
+)
+
+// SuiteSubtestRun detects situations like
+//
+// s.T().Run("subtest", func(t *testing.T) {
+// assert.Equal(t, 42, result)
+// })
+//
+// and requires
+//
+// s.Run("subtest", func() {
+// s.Equal(42, result)
+// })
+type SuiteSubtestRun struct{}
+
+// NewSuiteSubtestRun constructs SuiteSubtestRun checker.
+func NewSuiteSubtestRun() SuiteSubtestRun { return SuiteSubtestRun{} }
+func (SuiteSubtestRun) Name() string { return "suite-subtest-run" }
+
+func (checker SuiteSubtestRun) Check(pass *analysis.Pass, insp *inspector.Inspector) (diagnostics []analysis.Diagnostic) {
+ insp.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, func(node ast.Node) {
+ ce := node.(*ast.CallExpr) // s.T().Run
+
+ se, ok := ce.Fun.(*ast.SelectorExpr) // s.T() + .Run
+ if !ok {
+ return
+ }
+ if !isIdentWithName("Run", se.Sel) {
+ return
+ }
+
+ tCall, ok := se.X.(*ast.CallExpr) // s.T()
+ if !ok {
+ return
+ }
+ tCallSel, ok := tCall.Fun.(*ast.SelectorExpr) // s + .T()
+ if !ok {
+ return
+ }
+ if !isIdentWithName("T", tCallSel.Sel) {
+ return
+ }
+
+ if implementsTestifySuite(pass, tCallSel.X) && implementsTestingT(pass, tCall) {
+ msg := fmt.Sprintf("use %s.Run to run subtest", analysisutil.NodeString(pass.Fset, tCallSel.X))
+ diagnostics = append(diagnostics, *newDiagnostic(checker.Name(), ce, msg, nil))
+ }
+ })
+ return diagnostics
+}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_thelper.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_thelper.go
index 5cadc93ad..59455290d 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_thelper.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_thelper.go
@@ -3,13 +3,11 @@ package checkers
import (
"fmt"
"go/ast"
- "strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/ast/inspector"
"github.com/Antonboom/testifylint/internal/analysisutil"
- "github.com/Antonboom/testifylint/internal/testify"
)
// SuiteTHelper requires t.Helper() call in suite helpers:
@@ -27,15 +25,15 @@ func (SuiteTHelper) Name() string { return "suite-thelper" }
func (checker SuiteTHelper) Check(pass *analysis.Pass, inspector *inspector.Inspector) (diagnostics []analysis.Diagnostic) {
inspector.Preorder([]ast.Node{(*ast.FuncDecl)(nil)}, func(node ast.Node) {
fd := node.(*ast.FuncDecl)
- if !isTestifySuiteMethod(pass, fd) {
+ if !isSuiteMethod(pass, fd) {
return
}
- if ident := fd.Name; ident == nil || isTestMethod(ident.Name) || isServiceMethod(ident.Name) {
+ if ident := fd.Name; ident == nil || isSuiteTestMethod(ident.Name) || isSuiteServiceMethod(ident.Name) {
return
}
- if !containsSuiteAssertions(pass, fd) {
+ if !fnContainsAssertions(pass, fd) {
return
}
@@ -67,64 +65,3 @@ func (checker SuiteTHelper) Check(pass *analysis.Pass, inspector *inspector.Insp
})
return diagnostics
}
-
-func isTestifySuiteMethod(pass *analysis.Pass, fDecl *ast.FuncDecl) bool {
- if fDecl.Recv == nil || len(fDecl.Recv.List) != 1 {
- return false
- }
-
- rcv := fDecl.Recv.List[0]
- return implementsTestifySuiteIface(pass, rcv.Type)
-}
-
-func isTestMethod(name string) bool {
- return strings.HasPrefix(name, "Test")
-}
-
-func isServiceMethod(name string) bool {
- // https://github.com/stretchr/testify/blob/master/suite/interfaces.go
- switch name {
- case "T", "SetT", "SetS", "SetupSuite", "SetupTest", "TearDownSuite", "TearDownTest",
- "BeforeTest", "AfterTest", "HandleStats", "SetupSubTest", "TearDownSubTest":
- return true
- }
- return false
-}
-
-func containsSuiteAssertions(pass *analysis.Pass, fn *ast.FuncDecl) bool {
- if fn.Body == nil {
- return false
- }
-
- for _, s := range fn.Body.List {
- if isSuiteAssertion(pass, s) {
- return true
- }
- }
- return false
-}
-
-func isSuiteAssertion(pass *analysis.Pass, stmt ast.Stmt) bool {
- expr, ok := stmt.(*ast.ExprStmt)
- if !ok {
- return false
- }
-
- ce, ok := expr.X.(*ast.CallExpr)
- if !ok {
- return false
- }
-
- se, ok := ce.Fun.(*ast.SelectorExpr)
- if !ok || se.Sel == nil {
- return false
- }
-
- if sel, ok := pass.TypesInfo.Selections[se]; ok {
- pkg := sel.Obj().Pkg()
- isAssert := analysisutil.IsPkg(pkg, testify.AssertPkgName, testify.AssertPkgPath)
- isRequire := analysisutil.IsPkg(pkg, testify.RequirePkgName, testify.RequirePkgPath)
- return isAssert || isRequire
- }
- return false
-}
diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/useless_assert.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/useless_assert.go
index 669f9d187..6f206d095 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/checkers/useless_assert.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/useless_assert.go
@@ -1,6 +1,8 @@
package checkers
import (
+ "go/ast"
+
"golang.org/x/tools/go/analysis"
"github.com/Antonboom/testifylint/internal/analysisutil"
@@ -13,6 +15,8 @@ import (
// assert.Equal(t, tt.value, tt.value)
// assert.ElementsMatch(t, users, users)
// ...
+// assert.True(t, num > num)
+// assert.False(t, num == num)
//
// 2) Open for contribution...
type UselessAssert struct{}
@@ -22,6 +26,8 @@ func NewUselessAssert() UselessAssert { return UselessAssert{} }
func (UselessAssert) Name() string { return "useless-assert" }
func (checker UselessAssert) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic {
+ var first, second ast.Node
+
switch call.Fn.NameFTrimmed {
case
"Contains",
@@ -55,14 +61,25 @@ func (checker UselessAssert) Check(pass *analysis.Pass, call *CallMeta) *analysi
"Subset",
"WithinDuration",
"YAMLEq":
- default:
- return nil
- }
+ if len(call.Args) < 2 {
+ return nil
+ }
+ first, second = call.Args[0], call.Args[1]
+
+ case "True", "False":
+ if len(call.Args) < 1 {
+ return nil
+ }
- if len(call.Args) < 2 {
+ be, ok := call.Args[0].(*ast.BinaryExpr)
+ if !ok {
+ return nil
+ }
+ first, second = be.X, be.Y
+
+ default:
return nil
}
- first, second := call.Args[0], call.Args[1]
if analysisutil.NodeString(pass.Fset, first) == analysisutil.NodeString(pass.Fset, second) {
return newDiagnostic(checker.Name(), call, "asserting of the same variable", nil)
diff --git a/vendor/github.com/Antonboom/testifylint/internal/config/config.go b/vendor/github.com/Antonboom/testifylint/internal/config/config.go
index 7eba0ea32..a8812e6d0 100644
--- a/vendor/github.com/Antonboom/testifylint/internal/config/config.go
+++ b/vendor/github.com/Antonboom/testifylint/internal/config/config.go
@@ -15,9 +15,19 @@ func NewDefault() Config {
DisabledCheckers: nil,
DisableAll: false,
EnabledCheckers: nil,
+ BoolCompare: BoolCompareConfig{
+ IgnoreCustomTypes: false,
+ },
ExpectedActual: ExpectedActualConfig{
ExpVarPattern: RegexpValue{checkers.DefaultExpectedVarPattern},
},
+ Formatter: FormatterConfig{
+ CheckFormatString: true,
+ RequireFFuncs: false,
+ },
+ GoRequire: GoRequireConfig{
+ IgnoreHTTPHandlers: false,
+ },
RequireError: RequireErrorConfig{
FnPattern: RegexpValue{nil},
},
@@ -36,6 +46,8 @@ type Config struct {
BoolCompare BoolCompareConfig
ExpectedActual ExpectedActualConfig
+ Formatter FormatterConfig
+ GoRequire GoRequireConfig
RequireError RequireErrorConfig
SuiteExtraAssertCall SuiteExtraAssertCallConfig
}
@@ -50,6 +62,17 @@ type ExpectedActualConfig struct {
ExpVarPattern RegexpValue
}
+// FormatterConfig implements configuration of checkers.Formatter.
+type FormatterConfig struct {
+ CheckFormatString bool
+ RequireFFuncs bool
+}
+
+// GoRequireConfig implements configuration of checkers.GoRequire.
+type GoRequireConfig struct {
+ IgnoreHTTPHandlers bool
+}
+
// RequireErrorConfig implements configuration of checkers.RequireError.
type RequireErrorConfig struct {
FnPattern RegexpValue
@@ -97,12 +120,32 @@ func BindToFlags(cfg *Config, fs *flag.FlagSet) {
fs.BoolVar(&cfg.DisableAll, "disable-all", false, "disable all checkers")
fs.Var(&cfg.EnabledCheckers, "enable", "comma separated list of enabled checkers (in addition to enabled by default)")
- fs.BoolVar(&cfg.BoolCompare.IgnoreCustomTypes, "bool-compare.ignore-custom-types", false,
- "ignore user defined types (over builtin bool)")
- fs.Var(&cfg.ExpectedActual.ExpVarPattern, "expected-actual.pattern", "regexp for expected variable name")
- fs.Var(&cfg.RequireError.FnPattern, "require-error.fn-pattern", "regexp for error assertions that should only be analyzed")
+ fs.BoolVar(&cfg.BoolCompare.IgnoreCustomTypes,
+ "bool-compare.ignore-custom-types", false,
+ "to ignore user defined types (over builtin bool)")
+
+ fs.Var(&cfg.ExpectedActual.ExpVarPattern,
+ "expected-actual.pattern",
+ "regexp for expected variable name")
+
+ fs.BoolVar(&cfg.Formatter.CheckFormatString,
+ "formatter.check-format-string", true,
+ "to enable go vet's printf checks")
+ fs.BoolVar(&cfg.Formatter.RequireFFuncs,
+ "formatter.require-f-funcs", false,
+ "to require f-assertions if format string is used")
+
+ fs.BoolVar(&cfg.GoRequire.IgnoreHTTPHandlers,
+ "go-require.ignore-http-handlers", false,
+ "to ignore HTTP handlers (like http.HandlerFunc)")
+
+ fs.Var(&cfg.RequireError.FnPattern,
+ "require-error.fn-pattern",
+ "regexp for error assertions that should only be analyzed")
+
fs.Var(NewEnumValue(suiteExtraAssertCallModeAsString, &cfg.SuiteExtraAssertCall.Mode),
- "suite-extra-assert-call.mode", "to require or remove extra Assert() call")
+ "suite-extra-assert-call.mode",
+ "to require or remove extra Assert() call")
}
var suiteExtraAssertCallModeAsString = map[string]checkers.SuiteExtraAssertCallMode{