73 lines
1.9 KiB
Go
73 lines
1.9 KiB
Go
package tparallel
|
|
|
|
import (
|
|
"go/types"
|
|
|
|
"github.com/gostaticanalysis/analysisutil"
|
|
"golang.org/x/tools/go/analysis"
|
|
"golang.org/x/tools/go/analysis/passes/buildssa"
|
|
|
|
"github.com/moricho/tparallel/pkg/ssafunc"
|
|
)
|
|
|
|
const doc = "tparallel detects inappropriate usage of t.Parallel() method in your Go test codes."
|
|
|
|
// Analyzer analyzes Go test codes whether they use t.Parallel() appropriately
|
|
// by using SSA (Single Static Assignment)
|
|
var Analyzer = &analysis.Analyzer{
|
|
Name: "tparallel",
|
|
Doc: doc,
|
|
Run: run,
|
|
Requires: []*analysis.Analyzer{
|
|
buildssa.Analyzer,
|
|
},
|
|
}
|
|
|
|
func run(pass *analysis.Pass) (interface{}, error) {
|
|
ssaanalyzer := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
|
|
|
|
obj := analysisutil.ObjectOf(pass, "testing", "T")
|
|
if obj == nil {
|
|
// skip checking
|
|
return nil, nil
|
|
}
|
|
testTyp, testPkg := obj.Type(), obj.Pkg()
|
|
|
|
p, _, _ := types.LookupFieldOrMethod(testTyp, true, testPkg, "Parallel")
|
|
parallel, _ := p.(*types.Func)
|
|
c, _, _ := types.LookupFieldOrMethod(testTyp, true, testPkg, "Cleanup")
|
|
cleanup, _ := c.(*types.Func)
|
|
|
|
testMap := getTestMap(ssaanalyzer, testTyp) // ex. {Test1: [TestSub1, TestSub2], Test2: [TestSub1, TestSub2, TestSub3], ...}
|
|
for top, subs := range testMap {
|
|
if len(subs) == 0 {
|
|
continue
|
|
}
|
|
isParallelTop := ssafunc.IsCalled(top, parallel)
|
|
isPararellSub := false
|
|
for _, sub := range subs {
|
|
isPararellSub = ssafunc.IsCalled(sub, parallel)
|
|
if isPararellSub {
|
|
break
|
|
}
|
|
}
|
|
|
|
if ssafunc.IsDeferCalled(top) {
|
|
useCleanup := ssafunc.IsCalled(top, cleanup)
|
|
if isPararellSub && !useCleanup {
|
|
pass.Reportf(top.Pos(), "%s should use t.Cleanup instead of defer", top.Name())
|
|
}
|
|
}
|
|
|
|
if isParallelTop == isPararellSub {
|
|
continue
|
|
} else if isPararellSub {
|
|
pass.Reportf(top.Pos(), "%s should call t.Parallel on the top level as well as its subtests", top.Name())
|
|
} else if isParallelTop {
|
|
pass.Reportf(top.Pos(), "%s's subtests should call t.Parallel", top.Name())
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
}
|