Skip to content

Commit 406b1ce

Browse files
range-val-address: fix false positive (#554)
range-val-address: fix false positive (closes #554)
1 parent c383001 commit 406b1ce

File tree

3 files changed

+104
-5
lines changed

3 files changed

+104
-5
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a
440440
| [`function-result-limit`](./RULES_DESCRIPTIONS.md#function-result-limit) | int | Specifies the maximum number of results a function can return | no | no |
441441
| [`imports-blacklist`](./RULES_DESCRIPTIONS.md#imports-blacklist) | []string | Disallows importing the specified packages | no | no |
442442
| [`range-val-in-closure`](./RULES_DESCRIPTIONS.md#range-val-in-closure)| n/a | Warns if range value is used in a closure dispatched as goroutine| no | no |
443-
| [`range-val-address`](./RULES_DESCRIPTIONS.md#range-val-address)| n/a | Warns if address of range value is used dangerously | no | no |
443+
| [`range-val-address`](./RULES_DESCRIPTIONS.md#range-val-address)| n/a | Warns if address of range value is used dangerously | no | yes |
444444
| [`waitgroup-by-value`](./RULES_DESCRIPTIONS.md#waitgroup-by-value) | n/a | Warns on functions taking sync.WaitGroup as a by-value parameter | no | no |
445445
| [`atomic`](./RULES_DESCRIPTIONS.md#atomic) | n/a | Check for common mistaken usages of the `sync/atomic` package | no | no |
446446
| [`empty-lines`](./RULES_DESCRIPTIONS.md#empty-lines) | n/a | Warns when there are heading or trailing newlines in a block | no | no |

rule/range-val-address.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"go/ast"
66
"go/token"
7+
"strings"
78

89
"github.com/mgechev/revive/lint"
910
)
@@ -16,11 +17,13 @@ func (r *RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failur
1617
var failures []lint.Failure
1718

1819
walker := rangeValAddress{
20+
file: file,
1921
onFailure: func(failure lint.Failure) {
2022
failures = append(failures, failure)
2123
},
2224
}
2325

26+
file.Pkg.TypeCheck()
2427
ast.Walk(walker, file.AST)
2528

2629
return failures
@@ -32,6 +35,7 @@ func (r *RangeValAddress) Name() string {
3235
}
3336

3437
type rangeValAddress struct {
38+
file *lint.File
3539
onFailure func(lint.Failure)
3640
}
3741

@@ -46,17 +50,24 @@ func (w rangeValAddress) Visit(node ast.Node) ast.Visitor {
4650
return w
4751
}
4852

53+
valueIsStarExpr := false
54+
if t := w.file.Pkg.TypeOf(value); t != nil {
55+
valueIsStarExpr = strings.HasPrefix(t.String(), "*")
56+
}
57+
4958
ast.Walk(rangeBodyVisitor{
50-
valueID: value.Obj,
51-
onFailure: w.onFailure,
59+
valueIsStarExpr: valueIsStarExpr,
60+
valueID: value.Obj,
61+
onFailure: w.onFailure,
5262
}, n.Body)
5363

5464
return w
5565
}
5666

5767
type rangeBodyVisitor struct {
58-
valueID *ast.Object
59-
onFailure func(lint.Failure)
68+
valueIsStarExpr bool
69+
valueID *ast.Object
70+
onFailure func(lint.Failure)
6071
}
6172

6273
func (bw rangeBodyVisitor) Visit(node ast.Node) ast.Visitor {
@@ -112,6 +123,13 @@ func (bw rangeBodyVisitor) isAccessingRangeValueAddress(exp ast.Expr) bool {
112123
return false
113124
}
114125
v, ok = s.X.(*ast.Ident)
126+
if !ok {
127+
return false
128+
}
129+
130+
if bw.valueIsStarExpr { // check type of value
131+
return false
132+
}
115133
}
116134

117135
return ok && v.Obj == bw.valueID

testdata/range-val-address.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,84 @@ func rangeValAddress6() {
5757
m = append(m, &value.id) // MATCH /suspicious assignment of 'value'. range-loop variables always have the same address/
5858
}
5959
}
60+
61+
func rangeValAddress7() {
62+
type v struct {
63+
id string
64+
}
65+
m := []*string{}
66+
67+
for _, value := range []v{{id: "A"}, {id: "B"}, {id: "C"}} {
68+
m = append(m, &value.id) // MATCH /suspicious assignment of 'value'. range-loop variables always have the same address/
69+
}
70+
}
71+
72+
func rangeValAddress8() {
73+
type v struct {
74+
id string
75+
}
76+
m := []*string{}
77+
78+
mySlice := []*v{{id: "A"}, {id: "B"}, {id: "C"}}
79+
for _, value := range mySlice {
80+
m = append(m, &value.id)
81+
}
82+
}
83+
84+
func rangeValAddress9() {
85+
type v struct {
86+
id string
87+
}
88+
m := []*string{}
89+
90+
mySlice := map[string]*v{"a": {id: "A"}, "b": {id: "B"}, "c": {id: "C"}}
91+
for _, value := range mySlice {
92+
m = append(m, &value.id)
93+
}
94+
}
95+
96+
func rangeValAddress10() {
97+
type v struct {
98+
id string
99+
}
100+
m := []*string{}
101+
102+
for _, value := range map[string]*v{"a": {id: "A"}, "b": {id: "B"}, "c": {id: "C"}} {
103+
m = append(m, &value.id)
104+
}
105+
}
106+
107+
func rangeValAddress11() {
108+
type v struct {
109+
id string
110+
}
111+
m := map[string]*string{}
112+
113+
for key, value := range map[string]*v{"a": {id: "A"}, "b": {id: "B"}, "c": {id: "C"}} {
114+
m[key] = &value.id
115+
}
116+
}
117+
118+
func rangeValAddress12() {
119+
type v struct {
120+
id string
121+
}
122+
m := map[string]*string{}
123+
124+
for key, value := range map[string]v{"a": {id: "A"}, "b": {id: "B"}, "c": {id: "C"}} {
125+
m[key] = &value.id // MATCH /suspicious assignment of 'value'. range-loop variables always have the same address/
126+
}
127+
}
128+
129+
func rangeValAddress13() {
130+
type v struct {
131+
id string
132+
}
133+
m := []*string{}
134+
135+
otherSlice := map[string]*v{"a": {id: "A"}, "b": {id: "B"}, "c": {id: "C"}}
136+
mySlice := otherSlice
137+
for _, value := range mySlice {
138+
m = append(m, &value.id)
139+
}
140+
}

0 commit comments

Comments
 (0)