diff options
| author | Taras Madan <tarasmadan@google.com> | 2024-11-11 11:41:38 +0100 |
|---|---|---|
| committer | Taras Madan <tarasmadan@google.com> | 2024-11-11 11:10:48 +0000 |
| commit | 27e76fae2ee2d84dc7db63af1d9ed7358ba35b7a (patch) | |
| tree | ed19c0e35e272b3c4cc5a2f2c595e035b2428337 /vendor/github.com/Antonboom | |
| parent | 621e84e063b0e15b23e17780338627c509e1b9e8 (diff) | |
vendor: update
Diffstat (limited to 'vendor/github.com/Antonboom')
38 files changed, 853 insertions, 463 deletions
diff --git a/vendor/github.com/Antonboom/errname/pkg/analyzer/analyzer.go b/vendor/github.com/Antonboom/errname/pkg/analyzer/analyzer.go index aa8522510..2b8794dc2 100644 --- a/vendor/github.com/Antonboom/errname/pkg/analyzer/analyzer.go +++ b/vendor/github.com/Antonboom/errname/pkg/analyzer/analyzer.go @@ -1,11 +1,9 @@ package analyzer import ( - "fmt" "go/ast" "go/token" - "strconv" - "strings" + "go/types" "unicode" "golang.org/x/tools/go/analysis" @@ -23,86 +21,61 @@ func New() *analysis.Analyzer { } } -type stringSet = map[string]struct{} - -var ( - importNodes = []ast.Node{(*ast.ImportSpec)(nil)} - typeNodes = []ast.Node{(*ast.TypeSpec)(nil)} - funcNodes = []ast.Node{(*ast.FuncDecl)(nil)} -) - func run(pass *analysis.Pass) (interface{}, error) { insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - pkgAliases := map[string]string{} - insp.Preorder(importNodes, func(node ast.Node) { - i := node.(*ast.ImportSpec) - if n := i.Name; n != nil && i.Path != nil { - if path, err := strconv.Unquote(i.Path.Value); err == nil { - pkgAliases[n.Name] = getPkgFromPath(path) - } - } - }) - - allTypes := stringSet{} - typesSpecs := map[string]*ast.TypeSpec{} - insp.Preorder(typeNodes, func(node ast.Node) { - t := node.(*ast.TypeSpec) - allTypes[t.Name.Name] = struct{}{} - typesSpecs[t.Name.Name] = t - }) - - errorTypes := stringSet{} - insp.Preorder(funcNodes, func(node ast.Node) { - f := node.(*ast.FuncDecl) - t, ok := isMethodError(f) - if !ok { - return - } - errorTypes[t] = struct{}{} - - tSpec, ok := typesSpecs[t] - if !ok { - panic(fmt.Sprintf("no specification for type %q", t)) - } - - if _, ok := tSpec.Type.(*ast.ArrayType); ok { - if !isValidErrorArrayTypeName(t) { - reportAboutErrorType(pass, tSpec.Pos(), t, true) - } - } else if !isValidErrorTypeName(t) { - reportAboutErrorType(pass, tSpec.Pos(), t, false) - } - }) - - errorFuncs := stringSet{} - insp.Preorder(funcNodes, func(node ast.Node) { - f := node.(*ast.FuncDecl) - if isFuncReturningErr(f.Type, allTypes, errorTypes) { - errorFuncs[f.Name.Name] = struct{}{} + insp.Nodes([]ast.Node{ + (*ast.TypeSpec)(nil), + (*ast.ValueSpec)(nil), + (*ast.FuncDecl)(nil), + }, func(node ast.Node, push bool) bool { + if !push { + return false } - }) - inspectPkgLevelVarsOnly := func(node ast.Node) bool { switch v := node.(type) { case *ast.FuncDecl: return false case *ast.ValueSpec: - if name, ok := isSentinelError(v, pkgAliases, allTypes, errorTypes, errorFuncs); ok && !isValidErrorVarName(name) { - reportAboutErrorVar(pass, v.Pos(), name) + if len(v.Names) != 1 { + return false + } + ident := v.Names[0] + + if exprImplementsError(pass, ident) && !isValidErrorVarName(ident.Name) { + reportAboutSentinelError(pass, v.Pos(), ident.Name) + } + return false + + case *ast.TypeSpec: + tt := pass.TypesInfo.TypeOf(v.Name) + if tt == nil { + return false + } + // NOTE(a.telyshev): Pointer is the hack against Error() method with pointer receiver. + if !typeImplementsError(types.NewPointer(tt)) { + return false } + + name := v.Name.Name + if _, ok := v.Type.(*ast.ArrayType); ok { + if !isValidErrorArrayTypeName(name) { + reportAboutArrayErrorType(pass, v.Pos(), name) + } + } else if !isValidErrorTypeName(name) { + reportAboutErrorType(pass, v.Pos(), name) + } + return false } + return true - } - for _, f := range pass.Files { - ast.Inspect(f, inspectPkgLevelVarsOnly) - } + }) return nil, nil //nolint:nilnil } -func reportAboutErrorType(pass *analysis.Pass, typePos token.Pos, typeName string, isArrayType bool) { +func reportAboutErrorType(pass *analysis.Pass, typePos token.Pos, typeName string) { var form string if unicode.IsLower([]rune(typeName)[0]) { form = "xxxError" @@ -110,26 +83,26 @@ func reportAboutErrorType(pass *analysis.Pass, typePos token.Pos, typeName strin form = "XxxError" } - if isArrayType { - form += "s" + pass.Reportf(typePos, "the error type name `%s` should conform to the `%s` format", typeName, form) +} + +func reportAboutArrayErrorType(pass *analysis.Pass, typePos token.Pos, typeName string) { + var forms string + if unicode.IsLower([]rune(typeName)[0]) { + forms = "`xxxErrors` or `xxxError`" + } else { + forms = "`XxxErrors` or `XxxError`" } - pass.Reportf(typePos, "the type name `%s` should conform to the `%s` format", typeName, form) + + pass.Reportf(typePos, "the error type name `%s` should conform to the %s format", typeName, forms) } -func reportAboutErrorVar(pass *analysis.Pass, pos token.Pos, varName string) { +func reportAboutSentinelError(pass *analysis.Pass, pos token.Pos, varName string) { var form string if unicode.IsLower([]rune(varName)[0]) { form = "errXxx" } else { form = "ErrXxx" } - pass.Reportf(pos, "the variable name `%s` should conform to the `%s` format", varName, form) -} - -func getPkgFromPath(p string) string { - idx := strings.LastIndex(p, "/") - if idx == -1 { - return p - } - return p[idx+1:] + pass.Reportf(pos, "the sentinel error name `%s` should conform to the `%s` format", varName, form) } diff --git a/vendor/github.com/Antonboom/errname/pkg/analyzer/facts.go b/vendor/github.com/Antonboom/errname/pkg/analyzer/facts.go index 06f8d61d8..04e14fb68 100644 --- a/vendor/github.com/Antonboom/errname/pkg/analyzer/facts.go +++ b/vendor/github.com/Antonboom/errname/pkg/analyzer/facts.go @@ -1,58 +1,22 @@ package analyzer import ( - "fmt" "go/ast" - "go/token" "go/types" "strings" "unicode" -) - -func isMethodError(f *ast.FuncDecl) (typeName string, ok bool) { - if f.Recv == nil || len(f.Recv.List) != 1 { - return "", false - } - if f.Name == nil || f.Name.Name != "Error" { - return "", false - } - if f.Type == nil || f.Type.Results == nil || len(f.Type.Results.List) != 1 { - return "", false - } - - returnType, ok := f.Type.Results.List[0].Type.(*ast.Ident) - if !ok { - return "", false - } - - var receiverType string - - unwrapIdentName := func(e ast.Expr) string { - switch v := e.(type) { - case *ast.Ident: - return v.Name - case *ast.IndexExpr: - if i, ok := v.X.(*ast.Ident); ok { - return i.Name - } - case *ast.IndexListExpr: - if i, ok := v.X.(*ast.Ident); ok { - return i.Name - } - } - panic(fmt.Errorf("unsupported Error() receiver type %q", types.ExprString(e))) - } + "golang.org/x/tools/go/analysis" +) - switch rt := f.Recv.List[0].Type; v := rt.(type) { - case *ast.Ident, *ast.IndexExpr, *ast.IndexListExpr: // SomeError, SomeError[T], SomeError[T1, T2, ...] - receiverType = unwrapIdentName(rt) +var errorIface = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) - case *ast.StarExpr: // *SomeError, *SomeError[T], *SomeError[T1, T2, ...] - receiverType = unwrapIdentName(v.X) - } +func exprImplementsError(pass *analysis.Pass, e ast.Expr) bool { + return typeImplementsError(pass.TypesInfo.TypeOf(e)) +} - return receiverType, returnType.Name == "string" +func typeImplementsError(t types.Type) bool { + return t != nil && types.Implements(t, errorIface) } func isValidErrorTypeName(s string) bool { @@ -77,153 +41,12 @@ func isValidErrorArrayTypeName(s string) bool { words := split(s) wordsCnt := wordsCount(words) - if wordsCnt["errors"] != 1 { - return false - } - return words[len(words)-1] == "errors" -} - -func isFuncReturningErr(fType *ast.FuncType, allTypes, errorTypes stringSet) bool { - if fType == nil || fType.Results == nil || len(fType.Results.List) != 1 { + if wordsCnt["errors"] != 1 && wordsCnt["error"] != 1 { return false } - var returnTypeName string - switch rt := fType.Results.List[0].Type.(type) { - case *ast.Ident: - returnTypeName = rt.Name - case *ast.StarExpr: - if i, ok := rt.X.(*ast.Ident); ok { - returnTypeName = i.Name - } - } - - return isErrorType(returnTypeName, allTypes, errorTypes) -} - -func isErrorType(tName string, allTypes, errorTypes stringSet) bool { - _, isUserType := allTypes[tName] - _, isErrType := errorTypes[tName] - return isErrType || (tName == "error" && !isUserType) -} - -var knownErrConstructors = stringSet{ - "fmt.Errorf": {}, - "errors.Errorf": {}, - "errors.New": {}, - "errors.Newf": {}, - "errors.NewWithDepth": {}, - "errors.NewWithDepthf": {}, - "errors.NewAssertionErrorWithWrappedErrf": {}, -} - -func isSentinelError( //nolint:gocognit,gocyclo - v *ast.ValueSpec, - pkgAliases map[string]string, - allTypes, errorTypes, errorFuncs stringSet, -) (varName string, ok bool) { - if len(v.Names) != 1 { - return "", false - } - varName = v.Names[0].Name - - switch vv := v.Type.(type) { - // var ErrEndOfFile error - // var ErrEndOfFile SomeErrType - case *ast.Ident: - if isErrorType(vv.Name, allTypes, errorTypes) { - return varName, true - } - - // var ErrEndOfFile *SomeErrType - case *ast.StarExpr: - if i, ok := vv.X.(*ast.Ident); ok && isErrorType(i.Name, allTypes, errorTypes) { - return varName, true - } - } - - if len(v.Values) != 1 { - return "", false - } - - switch vv := v.Values[0].(type) { - case *ast.CallExpr: - switch fun := vv.Fun.(type) { - // var ErrEndOfFile = errors.New("end of file") - case *ast.SelectorExpr: - pkg, ok := fun.X.(*ast.Ident) - if !ok { - return "", false - } - pkgFun := fun.Sel - - pkgName := pkg.Name - if a, ok := pkgAliases[pkgName]; ok { - pkgName = a - } - - _, ok = knownErrConstructors[pkgName+"."+pkgFun.Name] - return varName, ok - - // var ErrEndOfFile = newErrEndOfFile() - // var ErrEndOfFile = new(EndOfFileError) - // const ErrEndOfFile = constError("end of file") - // var statusCodeError = new(SomePtrError[string]) - case *ast.Ident: - if isErrorType(fun.Name, allTypes, errorTypes) { - return varName, true - } - - if _, ok := errorFuncs[fun.Name]; ok { - return varName, true - } - - if fun.Name == "new" && len(vv.Args) == 1 { - switch i := vv.Args[0].(type) { - case *ast.Ident: - return varName, isErrorType(i.Name, allTypes, errorTypes) - case *ast.IndexExpr: - if ii, ok := i.X.(*ast.Ident); ok { - return varName, isErrorType(ii.Name, allTypes, errorTypes) - } - } - } - - // var ErrEndOfFile = func() error { ... } - case *ast.FuncLit: - return varName, isFuncReturningErr(fun.Type, allTypes, errorTypes) - } - - // var ErrEndOfFile = &EndOfFileError{} - // var ErrOK = &SomePtrError[string]{Code: "200 OK"} - case *ast.UnaryExpr: - if vv.Op == token.AND { // & - if lit, ok := vv.X.(*ast.CompositeLit); ok { - switch i := lit.Type.(type) { - case *ast.Ident: - return varName, isErrorType(i.Name, allTypes, errorTypes) - case *ast.IndexExpr: - if ii, ok := i.X.(*ast.Ident); ok { - return varName, isErrorType(ii.Name, allTypes, errorTypes) - } - } - } - } - - // var ErrEndOfFile = EndOfFileError{} - // var ErrNotFound = SomeError[string]{Code: "Not Found"} - case *ast.CompositeLit: - switch i := vv.Type.(type) { - case *ast.Ident: - return varName, isErrorType(i.Name, allTypes, errorTypes) - case *ast.IndexExpr: - if ii, ok := i.X.(*ast.Ident); ok { - return varName, isErrorType(ii.Name, allTypes, errorTypes) - } - } - } - - return "", false + lastWord := words[len(words)-1] + return lastWord == "errors" || lastWord == "error" } func isValidErrorVarName(s string) bool { diff --git a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go index 5646ee909..5507d9546 100644 --- a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go +++ b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go @@ -15,7 +15,8 @@ const ( name = "nilnil" doc = "Checks that there is no simultaneous return of `nil` error and an invalid value." - reportMsg = "return both the `nil` error and invalid value: use a sentinel error instead" + nilNilReportMsg = "return both a `nil` error and an invalid value: use a sentinel error instead" + notNilNotNilReportMsg = "return both a non-nil error and a valid value: use separate returns instead" ) // New returns new nilnil analyzer. @@ -28,18 +29,22 @@ func New() *analysis.Analyzer { Run: n.run, Requires: []*analysis.Analyzer{inspect.Analyzer}, } - a.Flags.Var(&n.checkedTypes, "checked-types", "coma separated list") + a.Flags.Var(&n.checkedTypes, "checked-types", "comma separated list of return types to check") + a.Flags.BoolVar(&n.detectOpposite, "detect-opposite", false, + "in addition, detect opposite situation (simultaneous return of non-nil error and valid value)") return a } type nilNil struct { - checkedTypes checkedTypes + checkedTypes checkedTypes + detectOpposite bool } func newNilNil() *nilNil { return &nilNil{ - checkedTypes: newDefaultCheckedTypes(), + checkedTypes: newDefaultCheckedTypes(), + detectOpposite: false, } } @@ -87,22 +92,22 @@ func (n *nilNil) run(pass *analysis.Pass) (interface{}, error) { } ok, zv := n.isDangerNilType(fRes1Type) - if !(ok && isErrorType(fRes2Type)) { + if !(ok && implementsError(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 ((zv == zeroValueNil) && isNil(pass, retVal) && isNil(pass, retErr)) || + ((zv == zeroValueZero) && isZero(retVal) && isNil(pass, retErr)) { + pass.Reportf(v.Pos(), nilNilReportMsg) + return false } - if needWarn { - pass.Reportf(v.Pos(), reportMsg) + if n.detectOpposite && (((zv == zeroValueNil) && !isNil(pass, retVal) && !isNil(pass, retErr)) || + ((zv == zeroValueZero) && !isZero(retVal) && !isNil(pass, retErr))) { + pass.Reportf(v.Pos(), notNilNotNilReportMsg) + return false } } @@ -152,7 +157,7 @@ func (n *nilNil) isDangerNilType(t types.Type) (bool, zeroValue) { var errorIface = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) -func isErrorType(t types.Type) bool { +func implementsError(t types.Type) bool { _, ok := t.Underlying().(*types.Interface) return ok && types.Implements(t, errorIface) } diff --git a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/config.go b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/config.go index c9b8e3eed..90ae548f3 100644 --- a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/config.go +++ b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/config.go @@ -8,11 +8,11 @@ import ( func newDefaultCheckedTypes() checkedTypes { return checkedTypes{ - ptrType: {}, + chanType: {}, funcType: {}, ifaceType: {}, mapType: {}, - chanType: {}, + ptrType: {}, uintptrType: {}, unsafeptrType: {}, } diff --git a/vendor/github.com/Antonboom/testifylint/internal/analysisutil/encoded.go b/vendor/github.com/Antonboom/testifylint/internal/analysisutil/encoded.go new file mode 100644 index 000000000..cafc283e6 --- /dev/null +++ b/vendor/github.com/Antonboom/testifylint/internal/analysisutil/encoded.go @@ -0,0 +1,46 @@ +package analysisutil + +import "strings" + +var whitespaceRemover = strings.NewReplacer("\n", "", "\\n", "", "\t", "", "\\t", "", " ", "") + +// IsJSONLike returns true if the string has JSON format features. +// A positive result can be returned for invalid JSON as well. +func IsJSONLike(s string) bool { + s = whitespaceRemover.Replace(unescape(s)) + + var startMatch bool + for _, prefix := range []string{ + `{{`, `{[`, `{"`, + `[{{`, `[{[`, `[{"`, + } { + if strings.HasPrefix(s, prefix) { + startMatch = true + break + } + } + if !startMatch { + return false + } + + for _, keyValue := range []string{`":{`, `":[`, `":"`} { + if strings.Contains(s, keyValue) { + return true + } + } + return false + + // NOTE(a.telyshev): We do not check the end of the string, because this is usually a field for typos. + // And one of the reasons for using JSON-specific assertions is to catch typos like this. +} + +func unescape(s string) string { + s = strings.ReplaceAll(s, `\"`, `"`) + s = unquote(s, `"`) + s = unquote(s, "`") + return s +} + +func unquote(s string, q string) string { + return strings.TrimLeft(strings.TrimRight(s, q), q) +} diff --git a/vendor/github.com/Antonboom/testifylint/internal/analysisutil/file.go b/vendor/github.com/Antonboom/testifylint/internal/analysisutil/file.go index 3fc1f42b8..d55260918 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/analysisutil/file.go +++ b/vendor/github.com/Antonboom/testifylint/internal/analysisutil/file.go @@ -2,6 +2,7 @@ package analysisutil import ( "go/ast" + "slices" "strconv" ) @@ -17,11 +18,8 @@ func Imports(file *ast.File, pkgs ...string) bool { if err != nil { continue } - // NOTE(a.telyshev): Don't use `slices.Contains` to keep the minimum module version 1.20. - for _, pkg := range pkgs { // Small O(n). - if pkg == path { - return true - } + if slices.Contains(pkgs, path) { // Small O(n). + return true } } return false diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/blank_import.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/blank_import.go index 403691e27..56cd64e07 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/blank_import.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/blank_import.go @@ -53,7 +53,7 @@ func (checker BlankImport) Check(pass *analysis.Pass, _ *inspector.Inspector) (d } msg := fmt.Sprintf("avoid blank import of %s as it does nothing", pkg) - diagnostics = append(diagnostics, *newDiagnostic(checker.Name(), imp, msg, nil)) + diagnostics = append(diagnostics, *newDiagnostic(checker.Name(), imp, msg)) } } return diagnostics 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 d125c43f9..67959b633 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/bool_compare.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/bool_compare.go @@ -49,13 +49,11 @@ func (checker BoolCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis. } survivingArg = newBoolCast(survivingArg) } - return newUseFunctionDiagnostic(checker.Name(), call, proposed, - newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{ - Pos: replaceStart, - End: replaceEnd, - NewText: analysisutil.NodeBytes(pass.Fset, survivingArg), - }), - ) + return newUseFunctionDiagnostic(checker.Name(), call, proposed, analysis.TextEdit{ + Pos: replaceStart, + End: replaceEnd, + NewText: analysisutil.NodeBytes(pass.Fset, survivingArg), + }) } newUseTrueDiagnostic := func(survivingArg ast.Expr, replaceStart, replaceEnd token.Pos) *analysis.Diagnostic { @@ -74,7 +72,7 @@ func (checker BoolCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis. survivingArg = newBoolCast(survivingArg) } return newDiagnostic(checker.Name(), call, "need to simplify the assertion", - &analysis.SuggestedFix{ + analysis.SuggestedFix{ Message: "Simplify the assertion", TextEdits: []analysis.TextEdit{{ Pos: replaceStart, @@ -106,7 +104,7 @@ func (checker BoolCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis. case xor(t1, t2): survivingArg, _ := anyVal([]bool{t1, t2}, arg2, arg1) if call.Fn.NameFTrimmed == "Exactly" && !isBuiltinBool(pass, survivingArg) { - // NOTE(a.telyshev): `Exactly` assumes no type casting. + // NOTE(a.telyshev): `Exactly` assumes no type conversion. return nil } return newUseTrueDiagnostic(survivingArg, arg1.Pos(), arg2.End()) @@ -114,7 +112,7 @@ func (checker BoolCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis. case xor(f1, f2): survivingArg, _ := anyVal([]bool{f1, f2}, arg2, arg1) if call.Fn.NameFTrimmed == "Exactly" && !isBuiltinBool(pass, survivingArg) { - // NOTE(a.telyshev): `Exactly` assumes no type casting. + // NOTE(a.telyshev): `Exactly` assumes no type conversion. return nil } return newUseFalseDiagnostic(survivingArg, arg1.Pos(), arg2.End()) 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 17c7d14ee..f881be4f2 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go @@ -13,10 +13,13 @@ var registry = checkersRegistry{ {factory: asCheckerFactory(NewLen), enabledByDefault: true}, {factory: asCheckerFactory(NewNegativePositive), enabledByDefault: true}, {factory: asCheckerFactory(NewCompares), enabledByDefault: true}, + {factory: asCheckerFactory(NewContains), enabledByDefault: true}, {factory: asCheckerFactory(NewErrorNil), enabledByDefault: true}, {factory: asCheckerFactory(NewNilCompare), enabledByDefault: true}, {factory: asCheckerFactory(NewErrorIsAs), enabledByDefault: true}, + {factory: asCheckerFactory(NewEncodedCompare), enabledByDefault: true}, {factory: asCheckerFactory(NewExpectedActual), enabledByDefault: true}, + {factory: asCheckerFactory(NewRegexp), enabledByDefault: true}, {factory: asCheckerFactory(NewSuiteExtraAssertCall), enabledByDefault: true}, {factory: asCheckerFactory(NewSuiteDontUsePkg), enabledByDefault: true}, {factory: asCheckerFactory(NewUselessAssert), enabledByDefault: true}, diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/compares.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/compares.go index bdde03d95..f0c4013f1 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/compares.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/compares.go @@ -61,7 +61,9 @@ func (checker Compares) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia return nil } - if isPointer(pass, be.X) && isPointer(pass, be.Y) { + _, xp := isPointer(pass, be.X) + _, yp := isPointer(pass, be.Y) + if xp && yp { switch proposedFn { case "Equal": proposedFn = "Same" @@ -72,12 +74,11 @@ func (checker Compares) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia a, b := be.X, be.Y return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, - newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{ + analysis.TextEdit{ Pos: be.X.Pos(), End: be.Y.End(), NewText: formatAsCallArgs(pass, a, b), - }), - ) + }) } var tokenToProposedFnInsteadOfTrue = map[token.Token]string{ diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/contains.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/contains.go new file mode 100644 index 000000000..07f76c6e4 --- /dev/null +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/contains.go @@ -0,0 +1,71 @@ +package checkers + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" +) + +// Contains detects situations like +// +// assert.True(t, strings.Contains(a, "abc123")) +// assert.False(t, !strings.Contains(a, "abc123")) +// +// assert.False(t, strings.Contains(a, "abc123")) +// assert.True(t, !strings.Contains(a, "abc123")) +// +// and requires +// +// assert.Contains(t, a, "abc123") +// assert.NotContains(t, a, "abc123") +type Contains struct{} + +// NewContains constructs Contains checker. +func NewContains() Contains { return Contains{} } +func (Contains) Name() string { return "contains" } + +func (checker Contains) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic { + if len(call.Args) < 1 { + return nil + } + + expr := call.Args[0] + unpacked, isNeg := isNegation(expr) + if isNeg { + expr = unpacked + } + + ce, ok := expr.(*ast.CallExpr) + if !ok || len(ce.Args) != 2 { + return nil + } + + if !isStringsContainsCall(pass, ce) { + return nil + } + + var proposed string + switch call.Fn.NameFTrimmed { + default: + return nil + + case "True": + proposed = "Contains" + if isNeg { + proposed = "NotContains" + } + + case "False": + proposed = "NotContains" + if isNeg { + proposed = "Contains" + } + } + + return newUseFunctionDiagnostic(checker.Name(), call, proposed, + analysis.TextEdit{ + Pos: call.Args[0].Pos(), + End: call.Args[0].End(), + NewText: formatAsCallArgs(pass, ce.Args[0], ce.Args[1]), + }) +} diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go index eafecb678..71657fe11 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go @@ -53,25 +53,28 @@ func (checker Empty) checkEmpty(pass *analysis.Pass, call *CallMeta) *analysis.D newUseEmptyDiagnostic := func(replaceStart, replaceEnd token.Pos, replaceWith ast.Expr) *analysis.Diagnostic { const proposed = "Empty" return newUseFunctionDiagnostic(checker.Name(), call, proposed, - newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{ + analysis.TextEdit{ Pos: replaceStart, End: replaceEnd, NewText: analysisutil.NodeBytes(pass.Fset, replaceWith), - }), - ) + }) } 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 { + case "Zero": + if lenArg, ok := isBuiltinLenCall(pass, a); ok { return newUseEmptyDiagnostic(a.Pos(), a.End(), lenArg) } + + case "Empty": + if lenArg, ok := isBuiltinLenCall(pass, a); ok { + return newRemoveLenDiagnostic(pass, checker.Name(), call, a, lenArg) + } } if len(call.Args) < 2 { @@ -120,25 +123,28 @@ func (checker Empty) checkNotEmpty(pass *analysis.Pass, call *CallMeta) *analysi newUseNotEmptyDiagnostic := func(replaceStart, replaceEnd token.Pos, replaceWith ast.Expr) *analysis.Diagnostic { const proposed = "NotEmpty" return newUseFunctionDiagnostic(checker.Name(), call, proposed, - newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{ + analysis.TextEdit{ Pos: replaceStart, End: replaceEnd, NewText: analysisutil.NodeBytes(pass.Fset, replaceWith), - }), - ) + }) } 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 { + case "NotZero", "Positive": + if lenArg, ok := isBuiltinLenCall(pass, a); ok { return newUseNotEmptyDiagnostic(a.Pos(), a.End(), lenArg) } + + case "NotEmpty": + if lenArg, ok := isBuiltinLenCall(pass, a); ok { + return newRemoveLenDiagnostic(pass, checker.Name(), call, a, lenArg) + } } if len(call.Args) < 2 { diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/encoded_compare.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/encoded_compare.go new file mode 100644 index 000000000..53c74ac45 --- /dev/null +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/encoded_compare.go @@ -0,0 +1,101 @@ +package checkers + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" +) + +// EncodedCompare detects situations like +// +// assert.Equal(t, `{"foo": "bar"}`, body) +// assert.EqualValues(t, `{"foo": "bar"}`, body) +// assert.Exactly(t, `{"foo": "bar"}`, body) +// assert.Equal(t, expectedJSON, resultJSON) +// assert.Equal(t, expBodyConst, w.Body.String()) +// assert.Equal(t, fmt.Sprintf(`{"value":"%s"}`, hexString), result) +// assert.Equal(t, "{}", json.RawMessage(resp)) +// assert.Equal(t, expJSON, strings.Trim(string(resultJSONBytes), "\n")) // + Replace, ReplaceAll, TrimSpace +// +// assert.Equal(t, expectedYML, conf) +// +// and requires +// +// assert.JSONEq(t, `{"foo": "bar"}`, body) +// assert.YAMLEq(t, expectedYML, conf) +type EncodedCompare struct{} + +// NewEncodedCompare constructs EncodedCompare checker. +func NewEncodedCompare() EncodedCompare { return EncodedCompare{} } +func (EncodedCompare) Name() string { return "encoded-compare" } + +func (checker EncodedCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic { + switch call.Fn.NameFTrimmed { + case "Equal", "EqualValues", "Exactly": + default: + return nil + } + + if len(call.Args) < 2 { + return nil + } + lhs, rhs := call.Args[0], call.Args[1] + + a, aIsExplicitJSON := checker.unwrap(pass, call.Args[0]) + b, bIsExplicitJSON := checker.unwrap(pass, call.Args[1]) + + var proposed string + switch { + case aIsExplicitJSON, bIsExplicitJSON, isJSONStyleExpr(pass, a), isJSONStyleExpr(pass, b): + proposed = "JSONEq" + case isYAMLStyleExpr(a), isYAMLStyleExpr(b): + proposed = "YAMLEq" + } + + if proposed != "" { + return newUseFunctionDiagnostic(checker.Name(), call, proposed, + analysis.TextEdit{ + Pos: lhs.Pos(), + End: lhs.End(), + NewText: formatWithStringCastForBytes(pass, a), + }, + analysis.TextEdit{ + Pos: rhs.Pos(), + End: rhs.End(), + NewText: formatWithStringCastForBytes(pass, b), + }, + ) + } + return nil +} + +// unwrap unwraps expression from string, []byte, strings.Replace(All), strings.Trim(Space) and json.RawMessage conversions. +// Returns true in the second argument, if json.RawMessage was in the chain. +func (checker EncodedCompare) unwrap(pass *analysis.Pass, e ast.Expr) (ast.Expr, bool) { + ce, ok := e.(*ast.CallExpr) + if !ok { + return e, false + } + if len(ce.Args) == 0 { + return e, false + } + + if isJSONRawMessageCast(pass, ce) { + if isNil(ce.Args[0]) { // NOTE(a.telyshev): Ignore json.RawMessage(nil) case. + return checker.unwrap(pass, ce.Args[0]) + } + + v, _ := checker.unwrap(pass, ce.Args[0]) + return v, true + } + + if isIdentWithName("string", ce.Fun) || + isByteArray(ce.Fun) || + isStringsReplaceCall(pass, ce) || + isStringsReplaceAllCall(pass, ce) || + isStringsTrimCall(pass, ce) || + isStringsTrimSpaceCall(pass, ce) { + return checker.unwrap(pass, ce.Args[0]) + } + return e, false +} 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 ab92c2ec0..f2812c939 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 @@ -67,12 +67,11 @@ func (checker ErrorIsAs) Check(pass *analysis.Pass, call *CallMeta) *analysis.Di } if proposed != "" { return newUseFunctionDiagnostic(checker.Name(), call, proposed, - newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{ + analysis.TextEdit{ Pos: ce.Pos(), End: ce.End(), NewText: formatAsCallArgs(pass, ce.Args[0], ce.Args[1]), - }), - ) + }) } case "False": @@ -91,12 +90,11 @@ func (checker ErrorIsAs) Check(pass *analysis.Pass, call *CallMeta) *analysis.Di if isErrorsIsCall(pass, ce) { const proposed = "NotErrorIs" return newUseFunctionDiagnostic(checker.Name(), call, proposed, - newSuggestedFuncReplacement(call, proposed, analysis.TextEdit{ + analysis.TextEdit{ Pos: ce.Pos(), End: ce.End(), NewText: formatAsCallArgs(pass, ce.Args[0], ce.Args[1]), - }), - ) + }) } case "ErrorAs": @@ -127,15 +125,15 @@ func (checker ErrorIsAs) Check(pass *analysis.Pass, call *CallMeta) *analysis.Di pt, ok := tv.Type.Underlying().(*types.Pointer) if !ok { - return newDiagnostic(checker.Name(), call, defaultReport, nil) + return newDiagnostic(checker.Name(), call, defaultReport) } if pt.Elem() == errorType { - return newDiagnostic(checker.Name(), call, errorPtrReport, nil) + return newDiagnostic(checker.Name(), call, errorPtrReport) } _, isInterface := pt.Elem().Underlying().(*types.Interface) if !isInterface && !types.Implements(pt.Elem(), errorIface) { - return newDiagnostic(checker.Name(), call, defaultReport, nil) + return newDiagnostic(checker.Name(), call, defaultReport) } } return nil 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 1e56d222a..b9f28df21 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/error_nil.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/error_nil.go @@ -12,12 +12,16 @@ import ( // ErrorNil detects situations like // // assert.Nil(t, err) -// assert.NotNil(t, err) +// assert.Empty(t, err) +// assert.Zero(t, err) // assert.Equal(t, nil, err) // assert.EqualValues(t, nil, err) // assert.Exactly(t, nil, err) // assert.ErrorIs(t, err, nil) // +// assert.NotNil(t, err) +// assert.NotEmpty(t, err) +// assert.NotZero(t, err) // assert.NotEqual(t, nil, err) // assert.NotEqualValues(t, nil, err) // assert.NotErrorIs(t, err, nil) @@ -40,12 +44,12 @@ func (checker ErrorNil) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia proposedFn, survivingArg, replacementEndPos := func() (string, ast.Expr, token.Pos) { switch call.Fn.NameFTrimmed { - case "Nil": + case "Nil", "Empty", "Zero": if len(call.Args) >= 1 && isError(pass, call.Args[0]) { return noErrorFn, call.Args[0], call.Args[0].End() } - case "NotNil": + case "NotNil", "NotEmpty", "NotZero": if len(call.Args) >= 1 && isError(pass, call.Args[0]) { return errorFn, call.Args[0], call.Args[0].End() } @@ -81,12 +85,11 @@ func (checker ErrorNil) Check(pass *analysis.Pass, call *CallMeta) *analysis.Dia if proposedFn != "" { return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, - newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{ + analysis.TextEdit{ Pos: call.Args[0].Pos(), End: replacementEndPos, NewText: analysisutil.NodeBytes(pass.Fset, survivingArg), - }), - ) + }) } return 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 77784dc7b..351d675ce 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go @@ -87,7 +87,7 @@ func (checker ExpectedActual) Check(pass *analysis.Pass, call *CallMeta) *analys first, second := call.Args[0], call.Args[1] if checker.isWrongExpectedActualOrder(pass, first, second) { - return newDiagnostic(checker.Name(), call, "need to reverse actual and expected values", &analysis.SuggestedFix{ + return newDiagnostic(checker.Name(), call, "need to reverse actual and expected values", analysis.SuggestedFix{ Message: "Reverse actual and expected values", TextEdits: []analysis.TextEdit{ { 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 7436f9ca1..6bc22cd02 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go @@ -44,7 +44,7 @@ func (checker FloatCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis if call.Fn.IsFmt { format = "use %s.InEpsilonf (or InDeltaf)" } - return newDiagnostic(checker.Name(), call, fmt.Sprintf(format, call.SelectorXStr), nil) + return newDiagnostic(checker.Name(), call, fmt.Sprintf(format, call.SelectorXStr)) } return nil } diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go index 3401bb097..896b6bf5f 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go @@ -1,8 +1,6 @@ package checkers import ( - "fmt" - "go/ast" "go/types" "strconv" @@ -60,7 +58,7 @@ func (checker Formatter) Check(pass *analysis.Pass, call *CallMeta) (result *ana } func (checker Formatter) checkNotFmtAssertion(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic { - msgAndArgsPos, ok := isPrintfLikeCall(pass, call, call.Fn.Signature) + msgAndArgsPos, ok := isPrintfLikeCall(pass, call) if !ok { return nil } @@ -71,21 +69,15 @@ func (checker Formatter) checkNotFmtAssertion(pass *analysis.Pass, call *CallMet 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 newRemoveFnAndUseDiagnostic(pass, checker.Name(), call, fFunc, + "fmt.Sprintf", msgAndArgs, args...) } return newRemoveSprintfDiagnostic(pass, checker.Name(), call, msgAndArgs, args) } } if checker.requireFFuncs { - return newUseFunctionDiagnostic(checker.Name(), call, fFunc, newSuggestedFuncReplacement(call, fFunc)) + return newUseFunctionDiagnostic(checker.Name(), call, fFunc) } return nil } @@ -109,7 +101,7 @@ func (checker Formatter) checkFmtAssertion(pass *analysis.Pass, call *CallMeta) defer func() { pass.Report = report }() pass.Report = func(d analysis.Diagnostic) { - result = newDiagnostic(checker.Name(), call, d.Message, nil) + result = newDiagnostic(checker.Name(), call, d.Message) } format, err := strconv.Unquote(analysisutil.NodeString(pass.Fset, msg)) @@ -121,21 +113,51 @@ func (checker Formatter) checkFmtAssertion(pass *analysis.Pass, call *CallMeta) return result } -func isPrintfLikeCall(pass *analysis.Pass, call *CallMeta, sig *types.Signature) (int, bool) { - msgAndArgsPos := getMsgAndArgsPosition(sig) +func isPrintfLikeCall(pass *analysis.Pass, call *CallMeta) (int, bool) { + msgAndArgsPos := getMsgAndArgsPosition(call.Fn.Signature) 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. + if !assertHasFormattedAnalogue(pass, call) { return -1, false } return msgAndArgsPos, len(call.ArgsRaw) > msgAndArgsPos } +func assertHasFormattedAnalogue(pass *analysis.Pass, call *CallMeta) bool { + if fn := analysisutil.ObjectOf(pass.Pkg, testify.AssertPkgPath, call.Fn.Name+"f"); fn != nil { + return true + } + + if fn := analysisutil.ObjectOf(pass.Pkg, testify.RequirePkgPath, call.Fn.Name+"f"); fn != nil { + return true + } + + recv := call.Fn.Signature.Recv() + if recv == nil { + return false + } + + recvT := recv.Type() + if ptr, ok := recv.Type().(*types.Pointer); ok { + recvT = ptr.Elem() + } + + suite, ok := recvT.(*types.Named) + if !ok { + return false + } + for i := 0; i < suite.NumMethods(); i++ { + if suite.Method(i).Name() == call.Fn.Name+"f" { + return true + } + } + + return false +} + func getMsgAndArgsPosition(sig *types.Signature) int { params := sig.Params() if params.Len() < 1 { @@ -162,26 +184,3 @@ func getMsgPosition(sig *types.Signature) int { } 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 060c96033..8b0d39999 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/go_require.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/go_require.go @@ -142,11 +142,11 @@ func (checker GoRequire) Check(pass *analysis.Pass, inspector *inspector.Inspect if testifyCall != nil { switch checker.checkCall(testifyCall) { case goRequireVerdictRequire: - d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireCallReportFormat, "require"), nil) + d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireCallReportFormat, "require")) diagnostics = append(diagnostics, *d) case goRequireVerdictAssertFailNow: - d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireCallReportFormat, testifyCall), nil) + d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireCallReportFormat, testifyCall)) diagnostics = append(diagnostics, *d) case goRequireVerdictNoExit: @@ -163,7 +163,7 @@ func (checker GoRequire) Check(pass *analysis.Pass, inspector *inspector.Inspect if v := checker.checkFunc(pass, calledFd, testsDecls, processedFuncs); v != goRequireVerdictNoExit { caller := analysisutil.NodeString(pass.Fset, ce.Fun) - d := newDiagnostic(checker.Name(), ce, fmt.Sprintf(goRequireFnReportFormat, caller), nil) + d := newDiagnostic(checker.Name(), ce, fmt.Sprintf(goRequireFnReportFormat, caller)) diagnostics = append(diagnostics, *d) } } @@ -198,11 +198,11 @@ func (checker GoRequire) checkHTTPHandlers(pass *analysis.Pass, insp *inspector. switch checker.checkCall(testifyCall) { case goRequireVerdictRequire: - d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, "require"), nil) + d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, "require")) diagnostics = append(diagnostics, *d) case goRequireVerdictAssertFailNow: - d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, testifyCall), nil) + d := newDiagnostic(checker.Name(), testifyCall, fmt.Sprintf(goRequireHTTPHandlerReportFormat, testifyCall)) diagnostics = append(diagnostics, *d) case goRequireVerdictNoExit: 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 index 432a3032c..9b43e914c 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go @@ -1,10 +1,10 @@ package checkers import ( - "fmt" "go/ast" "go/token" "go/types" + "strconv" "golang.org/x/tools/go/analysis" ) @@ -56,9 +56,29 @@ func isTypedIntNumber(e ast.Expr, v int, types ...string) bool { return false } -func isIntNumber(e ast.Expr, v int) bool { +func isIntNumber(e ast.Expr, rhs int) bool { + lhs, ok := isIntBasicLit(e) + return ok && (lhs == rhs) +} + +func isNegativeIntNumber(e ast.Expr) bool { + v, ok := isIntBasicLit(e) + return ok && v < 0 +} + +func isPositiveIntNumber(e ast.Expr) bool { + v, ok := isIntBasicLit(e) + return ok && v > 0 +} + +func isEmptyStringLit(e ast.Expr) bool { + bl, ok := e.(*ast.BasicLit) + return ok && bl.Kind == token.STRING && bl.Value == `""` +} + +func isNotEmptyStringLit(e ast.Expr) bool { bl, ok := e.(*ast.BasicLit) - return ok && bl.Kind == token.INT && bl.Value == fmt.Sprintf("%d", v) + return ok && bl.Kind == token.STRING && bl.Value != `""` } func isBasicLit(e ast.Expr) bool { @@ -66,9 +86,27 @@ func isBasicLit(e ast.Expr) bool { return ok } -func isIntBasicLit(e ast.Expr) bool { +func isIntBasicLit(e ast.Expr) (int, bool) { + if un, ok := e.(*ast.UnaryExpr); ok { + if un.Op == token.SUB { + v, ok := isIntBasicLit(un.X) + return -1 * v, ok + } + } + bl, ok := e.(*ast.BasicLit) - return ok && bl.Kind == token.INT + if !ok { + return 0, false + } + if bl.Kind != token.INT { + return 0, false + } + + v, err := strconv.Atoi(bl.Value) + if err != nil { + return 0, false + } + return v, true } func isUntypedConst(pass *analysis.Pass, e ast.Expr) bool { @@ -98,12 +136,37 @@ func isUnderlying(pass *analysis.Pass, e ast.Expr, flag types.BasicInfo) bool { return ok && (bt.Info()&flag > 0) } -func isPointer(pass *analysis.Pass, e ast.Expr) bool { - _, ok := pass.TypesInfo.TypeOf(e).(*types.Pointer) - return ok +func isPointer(pass *analysis.Pass, e ast.Expr) (types.Type, bool) { + ptr, ok := pass.TypesInfo.TypeOf(e).(*types.Pointer) + if !ok { + return nil, false + } + return ptr.Elem(), true +} + +// isByteArray returns true if expression is `[]byte` itself. +func isByteArray(e ast.Expr) bool { + at, ok := e.(*ast.ArrayType) + return ok && isIdentWithName("byte", at.Elt) +} + +// hasBytesType returns true if the expression is of `[]byte` type. +func hasBytesType(pass *analysis.Pass, e ast.Expr) bool { + t := pass.TypesInfo.TypeOf(e) + if t == nil { + return false + } + + sl, ok := t.(*types.Slice) + if !ok { + return false + } + + el, ok := sl.Elem().(*types.Basic) + return ok && el.Kind() == types.Uint8 } -// untype returns v from type(v) expression or v itself if there is no type cast. +// untype returns v from type(v) expression or v itself if there is no type conversion. func untype(e ast.Expr) ast.Expr { ce, ok := e.(*ast.CallExpr) if !ok || len(ce.Args) != 1 { diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go index 3ae88a560..f12d87aa3 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go @@ -7,11 +7,32 @@ import ( "golang.org/x/tools/go/analysis" ) +func newRemoveFnAndUseDiagnostic( + pass *analysis.Pass, + checker string, + call *CallMeta, + proposedFn string, + removedFn string, + removedFnPos analysis.Range, + removedFnArgs ...ast.Expr, +) *analysis.Diagnostic { + f := proposedFn + if call.Fn.IsFmt { + f += "f" + } + msg := fmt.Sprintf("remove unnecessary %s and use %s.%s", removedFn, call.SelectorXStr, f) + + return newDiagnostic(checker, call, msg, + newSuggestedFuncRemoving(pass, removedFn, removedFnPos, removedFnArgs...), + newSuggestedFuncReplacement(call, proposedFn), + ) +} + func newUseFunctionDiagnostic( checker string, call *CallMeta, proposedFn string, - fix *analysis.SuggestedFix, + additionalEdits ...analysis.TextEdit, ) *analysis.Diagnostic { f := proposedFn if call.Fn.IsFmt { @@ -19,33 +40,57 @@ func newUseFunctionDiagnostic( } msg := fmt.Sprintf("use %s.%s", call.SelectorXStr, f) - return newDiagnostic(checker, call, msg, fix) + return newDiagnostic(checker, call, msg, + newSuggestedFuncReplacement(call, proposedFn, additionalEdits...)) +} + +func newRemoveLenDiagnostic( + pass *analysis.Pass, + checker string, + call *CallMeta, + fnPos analysis.Range, + fnArg ast.Expr, +) *analysis.Diagnostic { + return newRemoveFnDiagnostic(pass, checker, call, "len", fnPos, fnArg) +} + +func newRemoveMustCompileDiagnostic( + pass *analysis.Pass, + checker string, + call *CallMeta, + fnPos analysis.Range, + fnArg ast.Expr, +) *analysis.Diagnostic { + return newRemoveFnDiagnostic(pass, checker, call, "regexp.MustCompile", fnPos, fnArg) } func newRemoveSprintfDiagnostic( pass *analysis.Pass, checker string, call *CallMeta, - sprintfPos analysis.Range, - sprintfArgs []ast.Expr, + fnPos analysis.Range, + fnArgs []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...), - }, - }, - }) + return newRemoveFnDiagnostic(pass, checker, call, "fmt.Sprintf", fnPos, fnArgs...) +} + +func newRemoveFnDiagnostic( + pass *analysis.Pass, + checker string, + call *CallMeta, + fnName string, + fnPos analysis.Range, + fnArgs ...ast.Expr, +) *analysis.Diagnostic { + return newDiagnostic(checker, call, "remove unnecessary "+fnName, + newSuggestedFuncRemoving(pass, fnName, fnPos, fnArgs...)) } func newDiagnostic( checker string, rng analysis.Range, msg string, - fix *analysis.SuggestedFix, + fixes ...analysis.SuggestedFix, ) *analysis.Diagnostic { d := analysis.Diagnostic{ Pos: rng.Pos(), @@ -53,21 +98,39 @@ func newDiagnostic( Category: checker, Message: checker + ": " + msg, } - if fix != nil { - d.SuggestedFixes = []analysis.SuggestedFix{*fix} + if len(fixes) != 0 { + d.SuggestedFixes = fixes } return &d } +func newSuggestedFuncRemoving( + pass *analysis.Pass, + fnName string, + fnPos analysis.Range, + fnArgs ...ast.Expr, +) analysis.SuggestedFix { + return analysis.SuggestedFix{ + Message: fmt.Sprintf("Remove `%s`", fnName), + TextEdits: []analysis.TextEdit{ + { + Pos: fnPos.Pos(), + End: fnPos.End(), + NewText: formatAsCallArgs(pass, fnArgs...), + }, + }, + } +} + func newSuggestedFuncReplacement( call *CallMeta, proposedFn string, additionalEdits ...analysis.TextEdit, -) *analysis.SuggestedFix { +) analysis.SuggestedFix { if call.Fn.IsFmt { proposedFn += "f" } - return &analysis.SuggestedFix{ + return analysis.SuggestedFix{ Message: fmt.Sprintf("Replace `%s` with `%s`", call.Fn.Name, proposedFn), TextEdits: append([]analysis.TextEdit{ { diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_encoded.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_encoded.go new file mode 100644 index 000000000..35a497a72 --- /dev/null +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_encoded.go @@ -0,0 +1,40 @@ +package checkers + +import ( + "go/ast" + "go/token" + "regexp" + + "golang.org/x/tools/go/analysis" + + "github.com/Antonboom/testifylint/internal/analysisutil" +) + +var ( + jsonIdentRe = regexp.MustCompile(`json|JSON|Json`) + yamlIdentRe = regexp.MustCompile(`yaml|YAML|Yaml|yml|YML|Yml`) +) + +func isJSONStyleExpr(pass *analysis.Pass, e ast.Expr) bool { + if isIdentNamedAfterPattern(jsonIdentRe, e) { + return true + } + + if t, ok := pass.TypesInfo.Types[e]; ok && t.Value != nil { + return analysisutil.IsJSONLike(t.Value.String()) + } + + if bl, ok := e.(*ast.BasicLit); ok { + return bl.Kind == token.STRING && analysisutil.IsJSONLike(bl.Value) + } + + if args, ok := isFmtSprintfCall(pass, e); ok { + return isJSONStyleExpr(pass, args[0]) + } + + return false +} + +func isYAMLStyleExpr(e ast.Expr) bool { + return isIdentNamedAfterPattern(yamlIdentRe, e) +} diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_error.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_error.go index 55cd5fd05..859a39ee8 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_error.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_error.go @@ -5,8 +5,6 @@ import ( "go/types" "golang.org/x/tools/go/analysis" - - "github.com/Antonboom/testifylint/internal/analysisutil" ) var ( @@ -20,23 +18,9 @@ func isError(pass *analysis.Pass, expr ast.Expr) bool { } func isErrorsIsCall(pass *analysis.Pass, ce *ast.CallExpr) bool { - return isErrorsPkgFnCall(pass, ce, "Is") + return isPkgFnCall(pass, ce, "errors", "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) + return isPkgFnCall(pass, ce, "errors", "As") } diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go index 765fce527..d69c42860 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go @@ -3,6 +3,7 @@ package checkers import ( "bytes" "go/ast" + "strings" "golang.org/x/tools/go/analysis" @@ -24,3 +25,37 @@ func formatAsCallArgs(pass *analysis.Pass, args ...ast.Expr) []byte { } return buf.Bytes() } + +func formatWithStringCastForBytes(pass *analysis.Pass, e ast.Expr) []byte { + if !hasBytesType(pass, e) { + return analysisutil.NodeBytes(pass.Fset, e) + } + + if se, ok := isBufferBytesCall(pass, e); ok { + return []byte(analysisutil.NodeString(pass.Fset, se) + ".String()") + } + return []byte("string(" + analysisutil.NodeString(pass.Fset, e) + ")") +} + +func isBufferBytesCall(pass *analysis.Pass, e ast.Expr) (ast.Node, bool) { + ce, ok := e.(*ast.CallExpr) + if !ok { + return nil, false + } + + se, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + return nil, false + } + + if !isIdentWithName("Bytes", se.Sel) { + return nil, false + } + if t := pass.TypesInfo.TypeOf(se.X); t != nil { + // NOTE(a.telyshev): This is hack, because `bytes` package can be not imported, + // and we cannot do "true" comparison with `Buffer` object. + return se.X, strings.TrimPrefix(t.String(), "*") == "bytes.Buffer" + } + + return nil, false +} diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go index b0c0d1302..ad39c72d7 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go @@ -15,8 +15,11 @@ func isEmptyInterface(pass *analysis.Pass, expr ast.Expr) bool { if !ok { return false } + return isEmptyInterfaceType(t.Type) +} - iface, ok := t.Type.Underlying().(*types.Interface) +func isEmptyInterfaceType(t types.Type) bool { + iface, ok := t.Underlying().(*types.Interface) return ok && iface.NumMethods() == 0 } diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_pkg_func.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_pkg_func.go new file mode 100644 index 000000000..daf309339 --- /dev/null +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_pkg_func.go @@ -0,0 +1,59 @@ +package checkers + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" + + "github.com/Antonboom/testifylint/internal/analysisutil" +) + +func isFmtSprintfCall(pass *analysis.Pass, e ast.Expr) ([]ast.Expr, bool) { + ce, ok := e.(*ast.CallExpr) + if !ok { + return nil, false + } + return ce.Args, isPkgFnCall(pass, ce, "fmt", "Sprintf") +} + +func isJSONRawMessageCast(pass *analysis.Pass, ce *ast.CallExpr) bool { + return isPkgFnCall(pass, ce, "encoding/json", "RawMessage") +} + +func isRegexpMustCompileCall(pass *analysis.Pass, ce *ast.CallExpr) bool { + return isPkgFnCall(pass, ce, "regexp", "MustCompile") +} + +func isStringsContainsCall(pass *analysis.Pass, ce *ast.CallExpr) bool { + return isPkgFnCall(pass, ce, "strings", "Contains") +} + +func isStringsReplaceCall(pass *analysis.Pass, ce *ast.CallExpr) bool { + return isPkgFnCall(pass, ce, "strings", "Replace") +} + +func isStringsReplaceAllCall(pass *analysis.Pass, ce *ast.CallExpr) bool { + return isPkgFnCall(pass, ce, "strings", "ReplaceAll") +} + +func isStringsTrimCall(pass *analysis.Pass, ce *ast.CallExpr) bool { + return isPkgFnCall(pass, ce, "strings", "Trim") +} + +func isStringsTrimSpaceCall(pass *analysis.Pass, ce *ast.CallExpr) bool { + return isPkgFnCall(pass, ce, "strings", "TrimSpace") +} + +func isPkgFnCall(pass *analysis.Pass, ce *ast.CallExpr, pkg, fn string) bool { + se, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + return false + } + + fnObj := analysisutil.ObjectOf(pass.Pkg, pkg, fn) + if fnObj == nil { + return false + } + + return analysisutil.IsObj(pass.TypesInfo, se.Sel, fnObj) +} diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/len.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/len.go index 47330568c..c240a6174 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/len.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/len.go @@ -31,17 +31,16 @@ func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnost a, b := call.Args[0], call.Args[1] if lenArg, expectedLen, ok := xorLenCall(pass, a, b); ok { - if expectedLen == b && !isIntBasicLit(expectedLen) { + if _, ok := isIntBasicLit(expectedLen); (expectedLen == b) && !ok { // https://github.com/Antonboom/testifylint/issues/9 return nil } return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, - newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{ + analysis.TextEdit{ Pos: a.Pos(), End: b.End(), NewText: formatAsCallArgs(pass, lenArg, expectedLen), - }), - ) + }) } case "True": @@ -50,14 +49,16 @@ func (checker Len) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnost } expr := call.Args[0] - if lenArg, expectedLen, ok := isLenEquality(pass, expr); ok && isIntBasicLit(expectedLen) { + if lenArg, expectedLen, ok := isLenEquality(pass, expr); ok { + if _, ok := isIntBasicLit(expectedLen); !ok { + return nil + } return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, - newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{ + analysis.TextEdit{ Pos: expr.Pos(), End: expr.End(), NewText: formatAsCallArgs(pass, lenArg, expectedLen), - }), - ) + }) } } return nil diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_positive.go index 274021f67..a61bbdfcb 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_positive.go @@ -48,12 +48,11 @@ func (checker NegativePositive) checkNegative(pass *analysis.Pass, call *CallMet 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{ + 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. @@ -114,12 +113,11 @@ func (checker NegativePositive) checkPositive(pass *analysis.Pass, call *CallMet 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{ + analysis.TextEdit{ Pos: replaceStart, End: replaceEnd, NewText: analysisutil.NodeBytes(pass.Fset, replaceWith), - }), - ) + }) } switch call.Fn.NameFTrimmed { 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 47c4a7383..fc1adb7ea 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/nil_compare.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/nil_compare.go @@ -47,10 +47,9 @@ func (checker NilCompare) Check(pass *analysis.Pass, call *CallMeta) *analysis.D } return newUseFunctionDiagnostic(checker.Name(), call, proposedFn, - newSuggestedFuncReplacement(call, proposedFn, analysis.TextEdit{ + analysis.TextEdit{ Pos: call.Args[0].Pos(), End: call.Args[1].End(), NewText: analysisutil.NodeBytes(pass.Fset, survivingArg), - }), - ) + }) } diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go index cfb47b542..4f6e3f9c4 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go @@ -213,9 +213,9 @@ func isFormatter(typ types.Type) bool { types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune]) } -// isTypeParam reports whether t is a type parameter. +// isTypeParam reports whether t is a type parameter (or an alias of one). func isTypeParam(t types.Type) bool { - _, ok := t.(*types.TypeParam) + _, ok := types.Unalias(t).(*types.TypeParam) return ok } @@ -224,7 +224,7 @@ func isTypeParam(t types.Type) bool { // 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) + n, ok := types.Unalias(t).(*types.Named) if !ok { return false } diff --git a/vendor/github.com/Antonboom/testifylint/internal/checkers/regexp.go b/vendor/github.com/Antonboom/testifylint/internal/checkers/regexp.go new file mode 100644 index 000000000..d634b74bd --- /dev/null +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/regexp.go @@ -0,0 +1,44 @@ +package checkers + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" +) + +// Regexp detects situations like +// +// assert.Regexp(t, regexp.MustCompile(`\[.*\] DEBUG \(.*TestNew.*\): message`), out) +// assert.NotRegexp(t, regexp.MustCompile(`\[.*\] TRACE message`), out) +// +// and requires +// +// assert.Regexp(t, `\[.*\] DEBUG \(.*TestNew.*\): message`, out) +// assert.NotRegexp(t, `\[.*\] TRACE message`, out) +type Regexp struct{} + +// NewRegexp constructs Regexp checker. +func NewRegexp() Regexp { return Regexp{} } +func (Regexp) Name() string { return "regexp" } + +func (checker Regexp) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic { + switch call.Fn.NameFTrimmed { + default: + return nil + case "Regexp", "NotRegexp": + } + + if len(call.Args) < 1 { + return nil + } + + ce, ok := call.Args[0].(*ast.CallExpr) + if !ok || len(ce.Args) != 1 { + return nil + } + + if isRegexpMustCompileCall(pass, ce) { + return newRemoveMustCompileDiagnostic(pass, checker.Name(), call, ce, ce.Args[0]) + } + return nil +} 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 4303828fd..e4e30aaf4 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/require_error.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/require_error.go @@ -134,7 +134,7 @@ func (checker RequireError) Check(pass *analysis.Pass, inspector *inspector.Insp } diagnostics = append(diagnostics, - *newDiagnostic(checker.Name(), c.testifyCall, requireErrorReport, nil)) + *newDiagnostic(checker.Name(), c.testifyCall, requireErrorReport)) } } @@ -197,11 +197,10 @@ func findRootIf(stack []ast.Node) *ast.IfStmt { nearestIf, i := findNearestNodeWithIdx[*ast.IfStmt](stack) for ; i > 0; i-- { parent, ok := stack[i-1].(*ast.IfStmt) - if ok { - nearestIf = parent - } else { + if !ok { break } + nearestIf = parent } return nearestIf } 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 index f830fd2a5..4374c9359 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_broken_parallel.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_broken_parallel.go @@ -68,7 +68,7 @@ func (checker SuiteBrokenParallel) Check(pass *analysis.Pass, insp *inspector.In } nextLine := pass.Fset.Position(ce.Pos()).Line + 1 - d := newDiagnostic(checker.Name(), ce, report, &analysis.SuggestedFix{ + d := newDiagnostic(checker.Name(), ce, report, analysis.SuggestedFix{ Message: fmt.Sprintf("Remove `%s` call", analysisutil.NodeString(pass.Fset, ce)), TextEdits: []analysis.TextEdit{ { 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 6150ae78d..4fbfbe7e0 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 @@ -60,7 +60,7 @@ func (checker SuiteDontUsePkg) Check(pass *analysis.Pass, call *CallMeta) *analy } msg := fmt.Sprintf("use %s.%s", newSelector, call.Fn.Name) - return newDiagnostic(checker.Name(), call, msg, &analysis.SuggestedFix{ + return newDiagnostic(checker.Name(), call, msg, analysis.SuggestedFix{ Message: fmt.Sprintf("Replace `%s` with `%s`", call.SelectorXStr, newSelector), TextEdits: []analysis.TextEdit{ // Replace package function with suite method. 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 9adfe5190..fdea324fd 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 @@ -61,7 +61,7 @@ func (checker SuiteExtraAssertCall) Check(pass *analysis.Pass, call *CallMeta) * } msg := fmt.Sprintf("use an explicit %s.Assert().%s", analysisutil.NodeString(pass.Fset, x), call.Fn.Name) - return newDiagnostic(checker.Name(), call, msg, &analysis.SuggestedFix{ + return newDiagnostic(checker.Name(), call, msg, analysis.SuggestedFix{ Message: "Add `Assert()` call", TextEdits: []analysis.TextEdit{{ Pos: x.End(), @@ -85,7 +85,7 @@ func (checker SuiteExtraAssertCall) Check(pass *analysis.Pass, call *CallMeta) * } msg := fmt.Sprintf("need to simplify the assertion to %s.%s", analysisutil.NodeString(pass.Fset, se.X), call.Fn.Name) - return newDiagnostic(checker.Name(), call, msg, &analysis.SuggestedFix{ + return newDiagnostic(checker.Name(), call, msg, analysis.SuggestedFix{ Message: "Remove `Assert()` call", TextEdits: []analysis.TextEdit{{ Pos: se.Sel.Pos(), 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 index 67d9c252b..525d5ffd8 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_subtest_run.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_subtest_run.go @@ -53,7 +53,7 @@ func (checker SuiteSubtestRun) Check(pass *analysis.Pass, insp *inspector.Inspec 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)) + diagnostics = append(diagnostics, *newDiagnostic(checker.Name(), ce, msg)) } }) 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 59455290d..ef8d82132 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_thelper.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_thelper.go @@ -50,8 +50,8 @@ func (checker SuiteTHelper) Check(pass *analysis.Pass, inspector *inspector.Insp return } - msg := fmt.Sprintf("suite helper method must start with " + helperCallStr) - d := newDiagnostic(checker.Name(), fd, msg, &analysis.SuggestedFix{ + msg := "suite helper method must start with " + helperCallStr + d := newDiagnostic(checker.Name(), fd, msg, analysis.SuggestedFix{ Message: fmt.Sprintf("Insert `%s`", helperCallStr), TextEdits: []analysis.TextEdit{ { 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 6f206d095..045706e5d 100644 --- a/vendor/github.com/Antonboom/testifylint/internal/checkers/useless_assert.go +++ b/vendor/github.com/Antonboom/testifylint/internal/checkers/useless_assert.go @@ -10,15 +10,40 @@ import ( // UselessAssert detects useless asserts like // -// 1) Asserting of the same variable -// +// assert.Contains(t, tt.value, tt.value) +// assert.ElementsMatch(t, tt.value, tt.value) // assert.Equal(t, tt.value, tt.value) -// assert.ElementsMatch(t, users, users) +// assert.EqualExportedValues(t, tt.value, tt.value) // ... +// // assert.True(t, num > num) +// assert.True(t, num < num) +// assert.True(t, num >= num) +// assert.True(t, num <= num) +// assert.True(t, num == num) +// assert.True(t, num != num) +// +// assert.False(t, num > num) +// assert.False(t, num < num) +// assert.False(t, num >= num) +// assert.False(t, num <= num) // assert.False(t, num == num) +// assert.False(t, num != num) // -// 2) Open for contribution... +// assert.Empty(t, "") +// assert.False(t, false) +// assert.Implements(t, (*any)(nil), new(Conn)) +// assert.Negative(t, -42) +// assert.Nil(t, nil) +// assert.NoError(t, nil) +// assert.NotEmpty(t, "value") +// assert.NotZero(t, 42) +// assert.NotZero(t, "value") +// assert.Positive(t, 42) +// assert.True(t, true) +// assert.Zero(t, 0) +// assert.Zero(t, "") +// assert.Zero(t, nil) type UselessAssert struct{} // NewUselessAssert constructs UselessAssert checker. @@ -26,6 +51,58 @@ func NewUselessAssert() UselessAssert { return UselessAssert{} } func (UselessAssert) Name() string { return "useless-assert" } func (checker UselessAssert) Check(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic { + if d := checker.checkSameVars(pass, call); d != nil { + return d + } + + var isMeaningless bool + switch call.Fn.NameFTrimmed { + case "Empty": + isMeaningless = (len(call.Args) >= 1) && isEmptyStringLit(call.Args[0]) + + case "False": + isMeaningless = (len(call.Args) >= 1) && isUntypedFalse(pass, call.Args[0]) + + case "Implements": + if len(call.Args) < 2 { + return nil + } + + elem, ok := isPointer(pass, call.Args[0]) + isMeaningless = ok && isEmptyInterfaceType(elem) + + case "Negative": + isMeaningless = (len(call.Args) >= 1) && isNegativeIntNumber(call.Args[0]) + + case "Nil", "NoError": + isMeaningless = (len(call.Args) >= 1) && isNil(call.Args[0]) + + case "NotEmpty": + isMeaningless = (len(call.Args) >= 1) && isNotEmptyStringLit(call.Args[0]) + + case "NotZero": + isMeaningless = (len(call.Args) >= 1) && + (isNotEmptyStringLit(call.Args[0]) || + isNegativeIntNumber(call.Args[0]) || isPositiveIntNumber(call.Args[0])) + + case "Positive": + isMeaningless = (len(call.Args) >= 1) && isPositiveIntNumber(call.Args[0]) + + case "True": + isMeaningless = (len(call.Args) >= 1) && isUntypedTrue(pass, call.Args[0]) + + case "Zero": + isMeaningless = (len(call.Args) >= 1) && + (isZero(call.Args[0]) || isEmptyStringLit(call.Args[0]) || isNil(call.Args[0])) + } + + if isMeaningless { + return newDiagnostic(checker.Name(), call, "meaningless assertion") + } + return nil +} + +func (checker UselessAssert) checkSameVars(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic { var first, second ast.Node switch call.Fn.NameFTrimmed { @@ -82,7 +159,7 @@ func (checker UselessAssert) Check(pass *analysis.Pass, call *CallMeta) *analysi } if analysisutil.NodeString(pass.Fset, first) == analysisutil.NodeString(pass.Fset, second) { - return newDiagnostic(checker.Name(), call, "asserting of the same variable", nil) + return newDiagnostic(checker.Name(), call, "asserting of the same variable") } return nil } |
