aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/kulti
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2022-09-05 14:27:54 +0200
committerGitHub <noreply@github.com>2022-09-05 12:27:54 +0000
commitb2f2446b46bf02821d90ebedadae2bf7ae0e880e (patch)
tree923cf42842918d6bebca1d6bbdc08abed54d274d /vendor/github.com/kulti
parente6654faff4bcca4be92e9a8596fd4b77f747c39e (diff)
go.mod, vendor: update (#3358)
* go.mod, vendor: remove unnecessary dependencies Commands: 1. go mod tidy 2. go mod vendor * go.mod, vendor: update cloud.google.com/go Commands: 1. go get -u cloud.google.com/go 2. go mod tidy 3. go mod vendor * go.mod, vendor: update cloud.google.com/* Commands: 1. go get -u cloud.google.com/storage cloud.google.com/logging 2. go mod tidy 3. go mod vendor * go.mod, .golangci.yml, vendor: update *lint* Commands: 1. go get -u golang.org/x/tools github.com/golangci/golangci-lint@v1.47.0 2. go mod tidy 3. go mod vendor 4. edit .golangci.yml to suppress new errors (resolved in the same PR later) * all: fix lint errors hash.go: copy() recommended by gosimple parse.go: ent is never nil verifier.go: signal.Notify() with unbuffered channel is bad. Have no idea why. * .golangci.yml: adjust godot rules check-all is deprecated, but still work if you're hesitating too - I'll remove this commit
Diffstat (limited to 'vendor/github.com/kulti')
-rw-r--r--vendor/github.com/kulti/thelper/pkg/analyzer/analyzer.go370
-rw-r--r--vendor/github.com/kulti/thelper/pkg/analyzer/report.go18
2 files changed, 324 insertions, 64 deletions
diff --git a/vendor/github.com/kulti/thelper/pkg/analyzer/analyzer.go b/vendor/github.com/kulti/thelper/pkg/analyzer/analyzer.go
index 369d3ad56..a22fd6aca 100644
--- a/vendor/github.com/kulti/thelper/pkg/analyzer/analyzer.go
+++ b/vendor/github.com/kulti/thelper/pkg/analyzer/analyzer.go
@@ -26,6 +26,7 @@ Available checks
` + checkTName + ` - check *testing.T param has t name
Also available similar checks for benchmark and TB helpers: ` +
+ checkFBegin + `, ` + checkFFirst + `, ` + checkFName + `,` +
checkBBegin + `, ` + checkBFirst + `, ` + checkBName + `,` +
checkTBBegin + `, ` + checkTBFirst + `, ` + checkTBName + `
@@ -60,6 +61,7 @@ func (m enabledChecksValue) Set(s string) error {
for _, v := range ss {
switch v {
case checkTBegin, checkTFirst, checkTName,
+ checkFBegin, checkFFirst, checkFName,
checkBBegin, checkBFirst, checkBName,
checkTBBegin, checkTBFirst, checkTBName:
m[v] = struct{}{}
@@ -74,6 +76,9 @@ const (
checkTBegin = "t_begin"
checkTFirst = "t_first"
checkTName = "t_name"
+ checkFBegin = "f_begin"
+ checkFFirst = "f_first"
+ checkFName = "f_name"
checkBBegin = "b_begin"
checkBFirst = "b_first"
checkBName = "b_name"
@@ -94,6 +99,9 @@ func NewAnalyzer() *analysis.Analyzer {
checkTBegin: struct{}{},
checkTFirst: struct{}{},
checkTName: struct{}{},
+ checkFBegin: struct{}{},
+ checkFFirst: struct{}{},
+ checkFName: struct{}{},
checkBBegin: struct{}{},
checkBFirst: struct{}{},
checkBName: struct{}{},
@@ -118,29 +126,17 @@ func NewAnalyzer() *analysis.Analyzer {
}
func (t thelper) run(pass *analysis.Pass) (interface{}, error) {
- var ctxType types.Type
- ctxObj := analysisutil.ObjectOf(pass, "context", "Context")
- if ctxObj != nil {
- ctxType = ctxObj.Type()
- }
-
- tCheckOpts, ok := t.buildTestCheckFuncOpts(pass, ctxType)
- if !ok {
- return nil, nil
- }
-
- bCheckOpts, ok := t.buildBenchmarkCheckFuncOpts(pass, ctxType)
+ tCheckOpts, fCheckOpts, bCheckOpts, tbCheckOpts, ok := t.buildCheckFuncOpts(pass)
if !ok {
return nil, nil
}
- tbCheckOpts, ok := t.buildTBCheckFuncOpts(pass, ctxType)
+ inspect, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
if !ok {
return nil, nil
}
var reports reports
- inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.FuncDecl)(nil),
(*ast.FuncLit)(nil),
@@ -160,14 +156,28 @@ func (t thelper) run(pass *analysis.Pass) (interface{}, error) {
fd.Body = n.Body
fd.Name = n.Name
case *ast.CallExpr:
- reports.Filter(subtestPos(pass, n, tCheckOpts.tbRun))
- reports.Filter(subtestPos(pass, n, bCheckOpts.tbRun))
+ runSubtestExprs := extractSubtestExp(pass, n, tCheckOpts.subRun, tCheckOpts.subTestFuncType)
+ if len(runSubtestExprs) == 0 {
+ runSubtestExprs = extractSubtestExp(pass, n, bCheckOpts.subRun, bCheckOpts.subTestFuncType)
+ }
+ if len(runSubtestExprs) == 0 {
+ runSubtestExprs = extractSubtestFuzzExp(pass, n, fCheckOpts.subRun)
+ }
+
+ if len(runSubtestExprs) > 0 {
+ for _, expr := range runSubtestExprs {
+ reports.Filter(funcDefPosition(pass, expr))
+ }
+ } else {
+ reports.NoFilter(funcDefPosition(pass, n.Fun))
+ }
return
default:
return
}
checkFunc(pass, &reports, fd, tCheckOpts)
+ checkFunc(pass, &reports, fd, fCheckOpts)
checkFunc(pass, &reports, fd, bCheckOpts)
checkFunc(pass, &reports, fd, tbCheckOpts)
})
@@ -178,15 +188,46 @@ func (t thelper) run(pass *analysis.Pass) (interface{}, error) {
}
type checkFuncOpts struct {
- skipPrefix string
- varName string
- tbHelper types.Object
- tbRun types.Object
- tbType types.Type
- ctxType types.Type
- checkBegin bool
- checkFirst bool
- checkName bool
+ skipPrefix string
+ varName string
+ fnHelper types.Object
+ subRun types.Object
+ subTestFuncType types.Type
+ hpType types.Type
+ ctxType types.Type
+ checkBegin bool
+ checkFirst bool
+ checkName bool
+}
+
+func (t thelper) buildCheckFuncOpts(pass *analysis.Pass) (checkFuncOpts, checkFuncOpts, checkFuncOpts, checkFuncOpts, bool) {
+ var ctxType types.Type
+ ctxObj := analysisutil.ObjectOf(pass, "context", "Context")
+ if ctxObj != nil {
+ ctxType = ctxObj.Type()
+ }
+
+ tCheckOpts, ok := t.buildTestCheckFuncOpts(pass, ctxType)
+ if !ok {
+ return checkFuncOpts{}, checkFuncOpts{}, checkFuncOpts{}, checkFuncOpts{}, false
+ }
+
+ fCheckOpts, ok := t.buildFuzzCheckFuncOpts(pass, ctxType)
+ if !ok {
+ return checkFuncOpts{}, checkFuncOpts{}, checkFuncOpts{}, checkFuncOpts{}, false
+ }
+
+ bCheckOpts, ok := t.buildBenchmarkCheckFuncOpts(pass, ctxType)
+ if !ok {
+ return checkFuncOpts{}, checkFuncOpts{}, checkFuncOpts{}, checkFuncOpts{}, false
+ }
+
+ tbCheckOpts, ok := t.buildTBCheckFuncOpts(pass, ctxType)
+ if !ok {
+ return checkFuncOpts{}, checkFuncOpts{}, checkFuncOpts{}, checkFuncOpts{}, false
+ }
+
+ return tCheckOpts, fCheckOpts, bCheckOpts, tbCheckOpts, true
}
func (t thelper) buildTestCheckFuncOpts(pass *analysis.Pass, ctxType types.Type) (checkFuncOpts, bool) {
@@ -205,16 +246,48 @@ func (t thelper) buildTestCheckFuncOpts(pass *analysis.Pass, ctxType types.Type)
return checkFuncOpts{}, false
}
+ tType := types.NewPointer(tObj.Type())
+ tVar := types.NewVar(token.NoPos, nil, "t", tType)
+ return checkFuncOpts{
+ skipPrefix: "Test",
+ varName: "t",
+ fnHelper: tHelper,
+ subRun: tRun,
+ hpType: tType,
+ subTestFuncType: types.NewSignature(nil, types.NewTuple(tVar), nil, false),
+ ctxType: ctxType,
+ checkBegin: t.enabledChecks.Enabled(checkTBegin),
+ checkFirst: t.enabledChecks.Enabled(checkTFirst),
+ checkName: t.enabledChecks.Enabled(checkTName),
+ }, true
+}
+
+func (t thelper) buildFuzzCheckFuncOpts(pass *analysis.Pass, ctxType types.Type) (checkFuncOpts, bool) {
+ fObj := analysisutil.ObjectOf(pass, "testing", "F")
+ if fObj == nil {
+ return checkFuncOpts{}, true // fuzzing supports since go1.18, it's ok, that testig.F is missed.
+ }
+
+ fHelper, _, _ := types.LookupFieldOrMethod(fObj.Type(), true, fObj.Pkg(), "Helper")
+ if fHelper == nil {
+ return checkFuncOpts{}, false
+ }
+
+ tFuzz, _, _ := types.LookupFieldOrMethod(fObj.Type(), true, fObj.Pkg(), "Fuzz")
+ if tFuzz == nil {
+ return checkFuncOpts{}, false
+ }
+
return checkFuncOpts{
- skipPrefix: "Test",
- varName: "t",
- tbHelper: tHelper,
- tbRun: tRun,
- tbType: types.NewPointer(tObj.Type()),
+ skipPrefix: "Fuzz",
+ varName: "f",
+ fnHelper: fHelper,
+ subRun: tFuzz,
+ hpType: types.NewPointer(fObj.Type()),
ctxType: ctxType,
- checkBegin: t.enabledChecks.Enabled(checkTBegin),
- checkFirst: t.enabledChecks.Enabled(checkTFirst),
- checkName: t.enabledChecks.Enabled(checkTName),
+ checkBegin: t.enabledChecks.Enabled(checkFBegin),
+ checkFirst: t.enabledChecks.Enabled(checkFFirst),
+ checkName: t.enabledChecks.Enabled(checkFName),
}, true
}
@@ -234,16 +307,19 @@ func (t thelper) buildBenchmarkCheckFuncOpts(pass *analysis.Pass, ctxType types.
return checkFuncOpts{}, false
}
+ bType := types.NewPointer(bObj.Type())
+ bVar := types.NewVar(token.NoPos, nil, "b", bType)
return checkFuncOpts{
- skipPrefix: "Benchmark",
- varName: "b",
- tbHelper: bHelper,
- tbRun: bRun,
- tbType: types.NewPointer(bObj.Type()),
- ctxType: ctxType,
- checkBegin: t.enabledChecks.Enabled(checkBBegin),
- checkFirst: t.enabledChecks.Enabled(checkBFirst),
- checkName: t.enabledChecks.Enabled(checkBName),
+ skipPrefix: "Benchmark",
+ varName: "b",
+ fnHelper: bHelper,
+ subRun: bRun,
+ hpType: types.NewPointer(bObj.Type()),
+ subTestFuncType: types.NewSignature(nil, types.NewTuple(bVar), nil, false),
+ ctxType: ctxType,
+ checkBegin: t.enabledChecks.Enabled(checkBBegin),
+ checkFirst: t.enabledChecks.Enabled(checkBFirst),
+ checkName: t.enabledChecks.Enabled(checkBName),
}, true
}
@@ -261,8 +337,8 @@ func (t thelper) buildTBCheckFuncOpts(pass *analysis.Pass, ctxType types.Type) (
return checkFuncOpts{
skipPrefix: "",
varName: "tb",
- tbHelper: tbHelper,
- tbType: tbObj.Type(),
+ fnHelper: tbHelper,
+ hpType: tbObj.Type(),
ctxType: ctxType,
checkBegin: t.enabledChecks.Enabled(checkTBBegin),
checkFirst: t.enabledChecks.Enabled(checkTBFirst),
@@ -278,11 +354,15 @@ type funcDecl struct {
}
func checkFunc(pass *analysis.Pass, reports *reports, funcDecl funcDecl, opts checkFuncOpts) {
+ if !opts.checkFirst && !opts.checkBegin && !opts.checkName {
+ return
+ }
+
if opts.skipPrefix != "" && strings.HasPrefix(funcDecl.Name.Name, opts.skipPrefix) {
return
}
- p, pos, ok := searchFuncParam(pass, funcDecl, opts.tbType)
+ p, pos, ok := searchFuncParam(pass, funcDecl, opts.hpType)
if !ok {
return
}
@@ -296,7 +376,7 @@ func checkFunc(pass *analysis.Pass, reports *reports, funcDecl funcDecl, opts ch
}
if !checkFirstPassed {
- reports.Reportf(funcDecl.Pos, "parameter %s should be the first or after context.Context", opts.tbType)
+ reports.Reportf(funcDecl.Pos, "parameter %s should be the first or after context.Context", opts.hpType)
}
}
}
@@ -304,32 +384,30 @@ func checkFunc(pass *analysis.Pass, reports *reports, funcDecl funcDecl, opts ch
if len(p.Names) > 0 && p.Names[0].Name != "_" {
if opts.checkName {
if p.Names[0].Name != opts.varName {
- reports.Reportf(funcDecl.Pos, "parameter %s should have name %s", opts.tbType, opts.varName)
+ reports.Reportf(funcDecl.Pos, "parameter %s should have name %s", opts.hpType, opts.varName)
}
}
if opts.checkBegin {
- if len(funcDecl.Body.List) == 0 || !isTHelperCall(pass, funcDecl.Body.List[0], opts.tbHelper) {
+ if len(funcDecl.Body.List) == 0 || !isTHelperCall(pass, funcDecl.Body.List[0], opts.fnHelper) {
reports.Reportf(funcDecl.Pos, "test helper function should start from %s.Helper()", opts.varName)
}
}
}
}
+// searchFuncParam search a function param with desired type.
+// It returns the param field, its position, and true if something is found.
func searchFuncParam(pass *analysis.Pass, f funcDecl, p types.Type) (*ast.Field, int, bool) {
for i, f := range f.Type.Params.List {
- typeInfo, ok := pass.TypesInfo.Types[f.Type]
- if !ok {
- continue
- }
-
- if types.Identical(typeInfo.Type, p) {
+ if isExprHasType(pass, f.Type, p) {
return f, i, true
}
}
return nil, 0, false
}
+// isTHelperCall returns true if provided statement 's' is t.Helper() or b.Helper() call.
func isTHelperCall(pass *analysis.Pass, s ast.Stmt, tHelper types.Object) bool {
exprStmt, ok := s.(*ast.ExprStmt)
if !ok {
@@ -349,28 +427,122 @@ func isTHelperCall(pass *analysis.Pass, s ast.Stmt, tHelper types.Object) bool {
return isSelectorCall(pass, selExpr, tHelper)
}
-func subtestPos(pass *analysis.Pass, e *ast.CallExpr, tbRun types.Object) token.Pos {
+// extractSubtestExp analyzes that call expresion 'e' is t.Run or b.Run
+// and returns subtest function.
+func extractSubtestExp(
+ pass *analysis.Pass, e *ast.CallExpr, tbRun types.Object, testFuncType types.Type,
+) []ast.Expr {
selExpr, ok := e.Fun.(*ast.SelectorExpr)
if !ok {
- return token.NoPos
+ return nil
}
if !isSelectorCall(pass, selExpr, tbRun) {
- return token.NoPos
+ return nil
}
if len(e.Args) != 2 {
- return token.NoPos
+ return nil
+ }
+
+ if funcs := unwrapTestingFunctionBuilding(pass, e.Args[1], testFuncType); funcs != nil {
+ return funcs
+ }
+
+ return []ast.Expr{e.Args[1]}
+}
+
+// extractSubtestFuzzExp analyzes that call expresion 'e' is f.Fuzz
+// and returns subtest function.
+func extractSubtestFuzzExp(
+ pass *analysis.Pass, e *ast.CallExpr, fuzzRun types.Object,
+) []ast.Expr {
+ selExpr, ok := e.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return nil
+ }
+
+ if !isSelectorCall(pass, selExpr, fuzzRun) {
+ return nil
+ }
+
+ if len(e.Args) != 1 {
+ return nil
+ }
+
+ return []ast.Expr{e.Args[0]}
+}
+
+// unwrapTestingFunctionConstruction checks that expresion is build testing functions
+// and returns the result of building.
+func unwrapTestingFunctionBuilding(pass *analysis.Pass, expr ast.Expr, testFuncType types.Type) []ast.Expr {
+ callExpr, ok := expr.(*ast.CallExpr)
+ if !ok {
+ return nil
+ }
+
+ var funcDecl funcDecl
+ switch f := callExpr.Fun.(type) {
+ case *ast.FuncLit:
+ funcDecl.Body = f.Body
+ funcDecl.Type = f.Type
+ case *ast.Ident:
+ funObjDecl := findFunctionDeclaration(pass, f)
+ if funObjDecl == nil {
+ return nil
+ }
+
+ funcDecl.Body = funObjDecl.Body
+ funcDecl.Type = funObjDecl.Type
+ case *ast.SelectorExpr:
+ fd := findSelectorDeclaration(pass, f)
+ if fd == nil {
+ return nil
+ }
+
+ funcDecl.Body = fd.Body
+ funcDecl.Type = fd.Type
+ default:
+ return nil
}
- anonFunLit, ok := e.Args[1].(*ast.FuncLit)
+ results := funcDecl.Type.Results.List
+ if len(results) != 1 || !isExprHasType(pass, results[0].Type, testFuncType) {
+ return nil
+ }
+
+ var funcs []ast.Expr
+ ast.Inspect(funcDecl.Body, func(n ast.Node) bool {
+ if n == nil {
+ return false
+ }
+
+ if retStmt, ok := n.(*ast.ReturnStmt); ok {
+ if len(retStmt.Results) == 1 {
+ funcs = append(funcs, retStmt.Results[0])
+ }
+ }
+ return true
+ })
+
+ return funcs
+}
+
+// funcDefPosition returns a function's position.
+// It works with anonymous functions as well with function names.
+func funcDefPosition(pass *analysis.Pass, e ast.Expr) token.Pos {
+ anonFunLit, ok := e.(*ast.FuncLit)
if ok {
return anonFunLit.Pos()
}
- funIdent, ok := e.Args[1].(*ast.Ident)
+ funIdent, ok := e.(*ast.Ident)
if !ok {
- return token.NoPos
+ selExpr, ok := e.(*ast.SelectorExpr)
+ if !ok {
+ return token.NoPos
+ }
+ funIdent = selExpr.Sel
}
funDef, ok := pass.TypesInfo.Uses[funIdent]
@@ -381,6 +553,8 @@ func subtestPos(pass *analysis.Pass, e *ast.CallExpr, tbRun types.Object) token.
return funDef.Pos()
}
+// isSelectorCall checks is selExpr is a call expresion on specific callObj.
+// Useful to check Run() call for t.Run or b.Run.
func isSelectorCall(pass *analysis.Pass, selExpr *ast.SelectorExpr, callObj types.Object) bool {
sel, ok := pass.TypesInfo.Selections[selExpr]
if !ok {
@@ -389,3 +563,77 @@ func isSelectorCall(pass *analysis.Pass, selExpr *ast.SelectorExpr, callObj type
return sel.Obj() == callObj
}
+
+// isExprHasType returns true if expr has expected type.
+func isExprHasType(pass *analysis.Pass, expr ast.Expr, expType types.Type) bool {
+ typeInfo, ok := pass.TypesInfo.Types[expr]
+ if !ok {
+ return false
+ }
+
+ return types.Identical(typeInfo.Type, expType)
+}
+
+// findSelectorDeclaration returns function declaration called by selector expression.
+func findSelectorDeclaration(pass *analysis.Pass, expr *ast.SelectorExpr) *ast.FuncDecl {
+ xsel, ok := pass.TypesInfo.Selections[expr]
+ if !ok {
+ return nil
+ }
+
+ for _, file := range pass.Files {
+ for _, decl := range file.Decls {
+ fd, ok := decl.(*ast.FuncDecl)
+ if ok && fd.Recv != nil && len(fd.Recv.List) == 1 {
+ recvType, ok := fd.Recv.List[0].Type.(*ast.Ident)
+ if !ok {
+ continue
+ }
+
+ recvObj, ok := pass.TypesInfo.Uses[recvType]
+ if !ok {
+ continue
+ }
+
+ if !(types.Identical(recvObj.Type(), xsel.Recv())) {
+ continue
+ }
+
+ if fd.Name.Name == expr.Sel.Name {
+ return fd
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+// findFunctionDeclaration returns function declaration called by identity.
+func findFunctionDeclaration(pass *analysis.Pass, ident *ast.Ident) *ast.FuncDecl {
+ if ident.Obj != nil {
+ if funObjDecl, ok := ident.Obj.Decl.(*ast.FuncDecl); ok {
+ return funObjDecl
+ }
+ }
+
+ obj := pass.TypesInfo.ObjectOf(ident)
+ if obj == nil {
+ return nil
+ }
+
+ for _, file := range pass.Files {
+ for _, decl := range file.Decls {
+ funcDecl, ok := decl.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+
+ if funcDecl.Name.Pos() == obj.Pos() {
+ return funcDecl
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/kulti/thelper/pkg/analyzer/report.go b/vendor/github.com/kulti/thelper/pkg/analyzer/report.go
index 1dd3eec5f..4a23e36d5 100644
--- a/vendor/github.com/kulti/thelper/pkg/analyzer/report.go
+++ b/vendor/github.com/kulti/thelper/pkg/analyzer/report.go
@@ -7,8 +7,9 @@ import (
)
type reports struct {
- reports []report
- filter map[token.Pos]struct{}
+ reports []report
+ filter map[token.Pos]struct{}
+ nofilter map[token.Pos]struct{}
}
type report struct {
@@ -34,10 +35,21 @@ func (rr *reports) Filter(pos token.Pos) {
}
}
+func (rr *reports) NoFilter(pos token.Pos) {
+ if pos.IsValid() {
+ if rr.nofilter == nil {
+ rr.nofilter = make(map[token.Pos]struct{})
+ }
+ rr.nofilter[pos] = struct{}{}
+ }
+}
+
func (rr reports) Flush(pass *analysis.Pass) {
for _, r := range rr.reports {
if _, ok := rr.filter[r.pos]; ok {
- continue
+ if _, ok := rr.nofilter[r.pos]; !ok {
+ continue
+ }
}
pass.Reportf(r.pos, r.format, r.args...)
}