diff --git a/README.md b/README.md index c5d0047..dad939b 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ go vet -vettool=(which tenv) ./... ./main_test.go:11:2: os.Setenv() can be replaced by `t.Setenv()` in TestMain ``` -### option +### options -The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. +The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. By default, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. @@ -75,13 +75,20 @@ func helper() { ``` ```console -go vet -vettool=(which tenv) -tenv.all ./... +go vet -vettool=$(which tenv) -tenv.all ./... # a ./main_test.go:11:2: os.Setenv() can be replaced by `t.Setenv()` in TestMain ./main_test.go:19:2: os.Setenv() can be replaced by `testing.Setenv()` in helper ``` +The option `go` allows to specify Go version. If the version is not empty or lower than Go 1.17, the analysis will be skipped. + +```console +go vet -vettool=$(which tenv) -tenv.go 1.16 ./... + +Outputs nothing because specified Go 1.16 is lower than 1.17. + ## CI ### CircleCI diff --git a/tenv.go b/tenv.go index fcff98d..e677f17 100644 --- a/tenv.go +++ b/tenv.go @@ -1,7 +1,10 @@ package tenv import ( + "errors" + "fmt" "go/ast" + "strconv" "strings" "golang.org/x/tools/go/analysis" @@ -24,13 +27,27 @@ var Analyzer = &analysis.Analyzer{ var ( A = "all" aflag bool + + Go = "go" + goflag string ) func init() { Analyzer.Flags.BoolVar(&aflag, A, false, "the all option will run against all method in test file") + Analyzer.Flags.StringVar(&goflag, Go, "1.17", "Go version") } func run(pass *analysis.Pass) (interface{}, error) { + lower, err := goVersionLower117(goflag) + if err != nil { + return nil, err + } + + if lower { + // Do nothing because T.Setenv added in go1.17 + return nil, nil + } + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ @@ -211,3 +228,33 @@ func checkSelectorExprTarget(typ *ast.SelectorExpr) bool { targetName := x.Name + "." + typ.Sel.Name return targetName == "testing.TB" } + +// goVersionLower117 returns true if version is lower than Go 1.17 or empty. +// version must be in the format 'go1.17', '1.17'. +// In case of an invalid input returns not-nil error. +func goVersionLower117(version string) (bool, error) { + version = strings.TrimPrefix(version, "go") + if version == "" { + return false, nil + } + + parts := strings.Split(version, ".") + if len(parts) != 2 { + return false, errors.New(`go version must has format "go." or "."`) + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return false, fmt.Errorf("go version major part must be a number: %w", err) + } + if major < 1 { + return true, nil + } + + minor, err := strconv.Atoi(parts[1]) + if err != nil { + return false, fmt.Errorf("go version minor part must be a number: %w", err) + } + + return minor < 17, nil +} diff --git a/tenv_test.go b/tenv_test.go index c4789c0..52fec82 100644 --- a/tenv_test.go +++ b/tenv_test.go @@ -14,3 +14,37 @@ func TestAnalyzer(t *testing.T) { testdata := testutil.WithModules(t, analysistest.TestData(), nil) analysistest.Run(t, testdata, tenv.Analyzer, "a") } + +func TestAnalyzerGo116(t *testing.T) { + testdata := testutil.WithModules(t, analysistest.TestData(), nil) + a := tenv.Analyzer + a.Flags.Parse([]string{"-go", "1.16"}) + analysistest.Run(t, testdata, a, "go116") +} + +func TestRun(t *testing.T) { + t.Run("empty Go version", func(t *testing.T) { + for _, goVersion := range []string{ + "", "go", + } { + testdata := testutil.WithModules(t, analysistest.TestData(), nil) + a := tenv.Analyzer + a.Flags.Parse([]string{"-go", goVersion}) + analysistest.Run(t, testdata, a, "a") + } + }) + + t.Run("invalid Go version", func(t *testing.T) { + for _, goVersion := range []string{ + "go1", "goa.2", "go1.a", + } { + a := tenv.Analyzer + a.Flags.Parse([]string{"-go", goVersion}) + _, err := a.Run(nil) + + if err == nil { + t.Error("expected error, but got ") + } + } + }) +} diff --git a/testdata/src/go116/go.mod b/testdata/src/go116/go.mod new file mode 100644 index 0000000..f78d17a --- /dev/null +++ b/testdata/src/go116/go.mod @@ -0,0 +1,3 @@ +module go116 + +go 1.16 diff --git a/testdata/src/go116/go116_test.go b/testdata/src/go116/go116_test.go new file mode 100644 index 0000000..dc6554c --- /dev/null +++ b/testdata/src/go116/go116_test.go @@ -0,0 +1,12 @@ +package go116 + +import ( + "os" + "testing" +) + +func TestF(t *testing.T) { + os.Setenv("a", "b") // if -go = 1.16, "" + err := os.Setenv("a", "b") // if -go = 1.16, "" + _ = err +}