1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
package rule
import (
"fmt"
"go/ast"
"go/token"
"github.com/mgechev/revive/lint"
)
// RangeValAddress lints
type RangeValAddress struct{}
// Apply applies the rule to given file.
func (r *RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
var failures []lint.Failure
walker := rangeValAddress{
onFailure: func(failure lint.Failure) {
failures = append(failures, failure)
},
}
ast.Walk(walker, file.AST)
return failures
}
// Name returns the rule name.
func (r *RangeValAddress) Name() string {
return "range-val-address"
}
type rangeValAddress struct {
onFailure func(lint.Failure)
}
func (w rangeValAddress) Visit(node ast.Node) ast.Visitor {
n, ok := node.(*ast.RangeStmt)
if !ok {
return w
}
value, ok := n.Value.(*ast.Ident)
if !ok {
return w
}
ast.Walk(rangeBodyVisitor{
valueID: value.Obj,
onFailure: w.onFailure,
}, n.Body)
return w
}
type rangeBodyVisitor struct {
valueID *ast.Object
onFailure func(lint.Failure)
}
func (bw rangeBodyVisitor) Visit(node ast.Node) ast.Visitor {
asgmt, ok := node.(*ast.AssignStmt)
if !ok {
return bw
}
for _, exp := range asgmt.Lhs {
e, ok := exp.(*ast.IndexExpr)
if !ok {
continue
}
if bw.isAccessingRangeValueAddress(e.Index) { // e.g. a[&value]...
bw.onFailure(bw.newFailure(e.Index))
}
}
for _, exp := range asgmt.Rhs {
switch e := exp.(type) {
case *ast.UnaryExpr: // e.g. ...&value
if bw.isAccessingRangeValueAddress(e) {
bw.onFailure(bw.newFailure(e))
}
case *ast.CallExpr:
if fun, ok := e.Fun.(*ast.Ident); ok && fun.Name == "append" { // e.g. ...append(arr, &value)
for _, v := range e.Args {
if bw.isAccessingRangeValueAddress(v) {
bw.onFailure(bw.newFailure(e))
}
}
}
}
}
return bw
}
func (bw rangeBodyVisitor) isAccessingRangeValueAddress(exp ast.Expr) bool {
u, ok := exp.(*ast.UnaryExpr)
if !ok {
return false
}
v, ok := u.X.(*ast.Ident)
return ok && u.Op == token.AND && v.Obj == bw.valueID
}
func (bw rangeBodyVisitor) newFailure(node ast.Node) lint.Failure {
return lint.Failure{
Node: node,
Confidence: 1,
Failure: fmt.Sprintf("suspicious assignment of '%s'. range-loop variables always have the same address", bw.valueID.Name),
}
}
|