diff --git a/.golangci.reference.yml b/.golangci.reference.yml index 8e8823758879..63b2774b216e 100644 --- a/.golangci.reference.yml +++ b/.golangci.reference.yml @@ -2297,6 +2297,7 @@ linters: - ireturn - lll - loggercheck + - loopvar - maintidx - makezero - maligned @@ -2417,6 +2418,7 @@ linters: - ireturn - lll - loggercheck + - loopvar - maintidx - makezero - maligned diff --git a/go.mod b/go.mod index a75ebc7346ba..3cf2a62cf3ec 100644 --- a/go.mod +++ b/go.mod @@ -59,6 +59,7 @@ require ( github.com/jingyugao/rowserrcheck v1.1.1 github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af github.com/julz/importas v0.1.0 + github.com/karamaru-alpha/loopvar v1.0.4 github.com/kisielk/errcheck v1.6.3 github.com/kkHAIKE/contextcheck v1.1.4 github.com/kulti/thelper v0.6.3 diff --git a/go.sum b/go.sum index 5d70f209aad2..1ebf74c1045c 100644 --- a/go.sum +++ b/go.sum @@ -325,6 +325,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= +github.com/karamaru-alpha/loopvar v1.0.4 h1:fIum97+upQY21fNxXCi2KZ+ysDsKTGRYlhS7QG6hN6k= +github.com/karamaru-alpha/loopvar v1.0.4/go.mod h1:ayH86Bsn4k+Ou6dOqSkFssgZMPVoNciBbFs1ZlkblFw= github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM8= github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= diff --git a/pkg/golinters/loopvar.go b/pkg/golinters/loopvar.go new file mode 100644 index 000000000000..e9cb103e0e5c --- /dev/null +++ b/pkg/golinters/loopvar.go @@ -0,0 +1,19 @@ +package golinters + +import ( + "github.com/karamaru-alpha/loopvar" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewLoopVar() *goanalysis.Linter { + a := loopvar.Analyzer + + return goanalysis.NewLinter( + a.Name, + a.Doc, + []*analysis.Analyzer{a}, + nil, + ).WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index fd329ce57fcf..104b9c740776 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -620,6 +620,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithAlternativeNames("logrlint"). WithURL("https://github.com/timonwong/loggercheck"), + linter.NewConfig(golinters.NewLoopVar()). + WithSince("v1.56.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/karamaru-alpha/loopvar"), + linter.NewConfig(golinters.NewMaintIdx(maintIdxCfg)). WithSince("v1.44.0"). WithPresets(linter.PresetComplexity). diff --git a/test/testdata/loopvar.go b/test/testdata/loopvar.go new file mode 100644 index 000000000000..2de0fc1bb09b --- /dev/null +++ b/test/testdata/loopvar.go @@ -0,0 +1,38 @@ +//golangcitest:args -Eloopvar +package testdata + +import ( + "fmt" +) + +func foo() { + slice := []int{1, 2, 3} + fns := make([]func(), 0, len(slice)*2) + for i, v := range slice { + i := i // want `The loop variable "i" should not be copied \(Go 1.22~ or Go 1.21 GOEXPERIMENT=loopvar\)` + fns = append(fns, func() { + fmt.Print(i) + }) + _v := v // want `The loop variable "v" should not be copied \(Go 1.22~ or Go 1.21 GOEXPERIMENT=loopvar\)` + fns = append(fns, func() { + fmt.Print(_v) + }) + } + for _, fn := range fns { + fn() + } +} + +func bar() { + loopCount := 3 + fns := make([]func(), 0, loopCount) + for i := 1; i <= loopCount; i++ { + i := i // want `The loop variable "i" should not be copied \(Go 1.22~ or Go 1.21 GOEXPERIMENT=loopvar\)` + fns = append(fns, func() { + fmt.Print(i) + }) + } + for _, fn := range fns { + fn() + } +}