Skip to content

Commit 4242f24

Browse files
Add support for the new implementation of for loop variables in go 1.22. (#993)
* Add support for the new behaviour of for loops in go 1.22. Go 1.22 has changed the behaviour of for loops. Every iteration makes new loop variables. It is now safe to take their addresses because they are guaranteed to be unique. Similarly, it is now safe to capture loop variables in functions. * adds documentation for public function --------- Co-authored-by: chavacava <[email protected]>
1 parent bbe5eb7 commit 4242f24

File tree

7 files changed

+83
-31
lines changed

7 files changed

+83
-31
lines changed

go.mod

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
module github.com/mgechev/revive
22

3-
go 1.20
3+
go 1.21
44

55
require (
66
github.com/BurntSushi/toml v1.4.0
77
github.com/chavacava/garif v0.1.0
88
github.com/fatih/color v1.17.0
99
github.com/fatih/structtag v1.2.0
10+
github.com/hashicorp/go-version v1.7.0
1011
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517
1112
github.com/mitchellh/go-homedir v1.1.0
1213
github.com/olekukonko/tablewriter v0.0.5
@@ -19,6 +20,7 @@ require (
1920
github.com/mattn/go-colorable v0.1.13 // indirect
2021
github.com/mattn/go-isatty v0.0.20 // indirect
2122
github.com/mattn/go-runewidth v0.0.9 // indirect
23+
github.com/stretchr/testify v1.9.0 // indirect
2224
golang.org/x/sys v0.20.0 // indirect
2325
golang.org/x/text v0.14.0 // indirect
2426
)

go.sum

+4-23
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
2-
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
31
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
42
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
53
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
64
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
75
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
86
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
97
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10-
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
11-
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
128
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
139
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
1410
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
1511
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
12+
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
13+
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
1614
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
1715
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
1816
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@@ -37,32 +35,15 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
3735
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
3836
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
3937
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
40-
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
4138
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
39+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
40+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
4241
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4342
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
44-
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
45-
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
46-
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
47-
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
48-
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
49-
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
50-
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
51-
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
5243
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
5344
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
5445
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
5546
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
56-
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
57-
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
58-
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
59-
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
60-
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
61-
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
62-
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
63-
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
64-
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
65-
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
6647
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
6748
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
6849
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

lint/linter.go

+51-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@ package lint
33
import (
44
"bufio"
55
"bytes"
6+
"encoding/json"
67
"fmt"
78
"go/token"
89
"os"
10+
"os/exec"
11+
"path/filepath"
912
"regexp"
1013
"strconv"
14+
"strings"
1115
"sync"
16+
17+
goversion "github.com/hashicorp/go-version"
1218
)
1319

1420
// ReadFile defines an abstraction for reading files.
@@ -78,9 +84,19 @@ func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-cha
7884
}
7985

8086
func (l *Linter) lintPackage(filenames []string, ruleSet []Rule, config Config, failures chan Failure) error {
87+
if len(filenames) == 0 {
88+
return nil
89+
}
90+
91+
goVersion, err := detectGoVersion(filepath.Dir(filenames[0]))
92+
if err != nil {
93+
return err
94+
}
95+
8196
pkg := &Package{
82-
fset: token.NewFileSet(),
83-
files: map[string]*File{},
97+
fset: token.NewFileSet(),
98+
files: map[string]*File{},
99+
goVersion: goVersion,
84100
}
85101
for _, filename := range filenames {
86102
content, err := l.readFile(filename)
@@ -108,6 +124,39 @@ func (l *Linter) lintPackage(filenames []string, ruleSet []Rule, config Config,
108124
return nil
109125
}
110126

127+
func detectGoVersion(dir string) (ver *goversion.Version, err error) {
128+
// https://github.com/golang/go/issues/44753#issuecomment-790089020
129+
cmd := exec.Command("go", "list", "-m", "-json")
130+
cmd.Dir = dir
131+
132+
raw, err := cmd.Output()
133+
if err != nil {
134+
return nil, fmt.Errorf("command go list: %w", err)
135+
}
136+
137+
var v struct {
138+
GoMod string `json:"GoMod"`
139+
GoVersion string `json:"GoVersion"`
140+
}
141+
if err = json.Unmarshal(raw, &v); err != nil {
142+
return nil, fmt.Errorf("can't parse the output of go list: %w", err)
143+
}
144+
145+
if v.GoMod == "" {
146+
// this package is outside a module, so assume
147+
// an old-style source directory
148+
149+
if v := os.Getenv("GOVERSION"); v != "" {
150+
return goversion.NewVersion(strings.TrimPrefix(v, "go"))
151+
}
152+
153+
// assume the last version that does not have generics
154+
return goversion.Must(goversion.NewVersion("1.17")), nil
155+
}
156+
157+
return goversion.NewVersion(strings.TrimPrefix(v.GoVersion, "go"))
158+
}
159+
111160
// isGenerated reports whether the source file is generated code
112161
// according the rules from https://golang.org/s/generatedcode.
113162
// This is inherited from the original go lint.

lint/package.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import (
77
"go/types"
88
"sync"
99

10+
goversion "github.com/hashicorp/go-version"
11+
1012
"github.com/mgechev/revive/internal/typeparams"
1113
)
1214

1315
// Package represents a package in the project.
1416
type Package struct {
15-
fset *token.FileSet
16-
files map[string]*File
17+
fset *token.FileSet
18+
files map[string]*File
19+
goVersion *goversion.Version
1720

1821
typesPkg *types.Package
1922
typesInfo *types.Info
@@ -29,6 +32,8 @@ var (
2932
trueValue = 1
3033
falseValue = 2
3134
notSet = 3
35+
36+
go122 = goversion.Must(goversion.NewVersion("1.22"))
3237
)
3338

3439
// Files return package's files.
@@ -188,3 +193,8 @@ func (p *Package) lint(rules []Rule, config Config, failures chan Failure) {
188193
}
189194
wg.Wait()
190195
}
196+
197+
// IsAtLeastGo122 returns true if the Go version for this package is 1.22 or higher, false otherwise
198+
func (p *Package) IsAtLeastGo122() bool {
199+
return p.goVersion.GreaterThanOrEqual(go122)
200+
}

rule/datarace.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func (*DataRaceRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
1616
onFailure := func(failure lint.Failure) {
1717
failures = append(failures, failure)
1818
}
19-
w := lintDataRaces{onFailure: onFailure}
19+
w := lintDataRaces{onFailure: onFailure, go122for: file.Pkg.IsAtLeastGo122()}
2020

2121
ast.Walk(w, file.AST)
2222

@@ -30,6 +30,7 @@ func (*DataRaceRule) Name() string {
3030

3131
type lintDataRaces struct {
3232
onFailure func(failure lint.Failure)
33+
go122for bool
3334
}
3435

3536
func (w lintDataRaces) Visit(n ast.Node) ast.Visitor {
@@ -47,7 +48,7 @@ func (w lintDataRaces) Visit(n ast.Node) ast.Visitor {
4748
if results != nil {
4849
returnIDs = w.ExtractReturnIDs(results.List)
4950
}
50-
fl := &lintFunctionForDataRaces{onFailure: w.onFailure, returnIDs: returnIDs, rangeIDs: map[*ast.Object]struct{}{}}
51+
fl := &lintFunctionForDataRaces{onFailure: w.onFailure, returnIDs: returnIDs, rangeIDs: map[*ast.Object]struct{}{}, go122for: w.go122for}
5152
ast.Walk(fl, node.Body)
5253

5354
return nil
@@ -69,6 +70,7 @@ type lintFunctionForDataRaces struct {
6970
onFailure func(failure lint.Failure)
7071
returnIDs map[*ast.Object]struct{}
7172
rangeIDs map[*ast.Object]struct{}
73+
go122for bool
7274
}
7375

7476
func (w lintFunctionForDataRaces) Visit(node ast.Node) ast.Visitor {
@@ -118,7 +120,7 @@ func (w lintFunctionForDataRaces) Visit(node ast.Node) ast.Visitor {
118120
_, isReturnID := w.returnIDs[id.Obj]
119121

120122
switch {
121-
case isRangeID:
123+
case isRangeID && !w.go122for:
122124
w.onFailure(lint.Failure{
123125
Confidence: 1,
124126
Node: id,

rule/range-val-address.go

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ type RangeValAddress struct{}
1616
func (*RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
1717
var failures []lint.Failure
1818

19+
if file.Pkg.IsAtLeastGo122() {
20+
return failures
21+
}
22+
1923
walker := rangeValAddress{
2024
file: file,
2125
onFailure: func(failure lint.Failure) {

rule/range-val-in-closure.go

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ type RangeValInClosureRule struct{}
1414
func (*RangeValInClosureRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
1515
var failures []lint.Failure
1616

17+
if file.Pkg.IsAtLeastGo122() {
18+
return failures
19+
}
20+
1721
walker := rangeValInClosure{
1822
onFailure: func(failure lint.Failure) {
1923
failures = append(failures, failure)

0 commit comments

Comments
 (0)