@@ -12,111 +12,52 @@ import (
12
12
type ModifiesValRecRule struct {}
13
13
14
14
// Apply applies the rule to given file.
15
- func (* ModifiesValRecRule ) Apply (file * lint.File , _ lint.Arguments ) []lint.Failure {
15
+ func (r * ModifiesValRecRule ) Apply (file * lint.File , _ lint.Arguments ) []lint.Failure {
16
16
var failures []lint.Failure
17
17
18
- onFailure := func (failure lint.Failure ) {
19
- failures = append (failures , failure )
20
- }
21
-
22
- w := lintModifiesValRecRule {file : file , onFailure : onFailure }
23
18
file .Pkg .TypeCheck ()
24
- ast .Walk (w , file .AST )
25
-
26
- return failures
27
- }
28
-
29
- // Name returns the rule name.
30
- func (* ModifiesValRecRule ) Name () string {
31
- return "modifies-value-receiver"
32
- }
33
-
34
- type lintModifiesValRecRule struct {
35
- file * lint.File
36
- onFailure func (lint.Failure )
37
- }
38
-
39
- func (w lintModifiesValRecRule ) Visit (node ast.Node ) ast.Visitor {
40
- switch n := node .(type ) {
41
- case * ast.FuncDecl :
42
- if n .Recv == nil {
43
- return nil // skip, not a method
44
- }
45
-
46
- receiver := n .Recv .List [0 ]
47
- if _ , ok := receiver .Type .(* ast.StarExpr ); ok {
48
- return nil // skip, method with pointer receiver
19
+ for _ , decl := range file .AST .Decls {
20
+ funcDecl , ok := decl .(* ast.FuncDecl )
21
+ isAMethod := ok && funcDecl .Recv != nil
22
+ if ! isAMethod {
23
+ continue // skip, not a method
49
24
}
50
25
51
- if w .skipType (receiver .Type ) {
52
- return nil // skip, receiver is a map or array
53
- }
54
-
55
- if len (receiver .Names ) < 1 {
56
- return nil // skip, anonymous receiver
26
+ receiver := funcDecl .Recv .List [0 ]
27
+ if r .mustSkip (receiver , file .Pkg ) {
28
+ continue
57
29
}
58
30
59
31
receiverName := receiver .Names [0 ].Name
60
- if receiverName == "_" {
61
- return nil // skip, anonymous receiver
62
- }
63
-
64
- receiverAssignmentFinder := func (n ast.Node ) bool {
65
- // look for assignments with the receiver in the right hand
66
- assignment , ok := n .(* ast.AssignStmt )
67
- if ! ok {
68
- return false
69
- }
70
-
71
- for _ , exp := range assignment .Lhs {
72
- switch e := exp .(type ) {
73
- case * ast.IndexExpr : // receiver...[] = ...
74
- continue
75
- case * ast.StarExpr : // *receiver = ...
76
- continue
77
- case * ast.SelectorExpr : // receiver.field = ...
78
- name := w .getNameFromExpr (e .X )
79
- if name == "" || name != receiverName {
80
- continue
81
- }
82
- case * ast.Ident : // receiver := ...
83
- if e .Name != receiverName {
84
- continue
85
- }
86
- default :
87
- continue
88
- }
89
-
90
- return true
91
- }
92
-
93
- return false
94
- }
95
-
96
- assignmentsToReceiver := pick (n .Body , receiverAssignmentFinder )
32
+ assignmentsToReceiver := r .getAssignmentsToReceiver (receiverName , funcDecl .Body )
97
33
if len (assignmentsToReceiver ) == 0 {
98
- return nil // receiver is not modified
34
+ continue // receiver is not modified
99
35
}
100
36
101
- methodReturnsReceiver := len (w .findReturnReceiverStatements (receiverName , n .Body )) > 0
37
+ methodReturnsReceiver := len (r .findReturnReceiverStatements (receiverName , funcDecl .Body )) > 0
102
38
if methodReturnsReceiver {
103
- return nil // modification seems legit (see issue #1066)
39
+ continue // modification seems legit (see issue #1066)
104
40
}
105
41
106
42
for _ , assignment := range assignmentsToReceiver {
107
- w . onFailure ( lint.Failure {
43
+ failures = append ( failures , lint.Failure {
108
44
Node : assignment ,
109
45
Confidence : 1 ,
110
46
Failure : "suspicious assignment to a by-value method receiver" ,
111
47
})
112
48
}
113
49
}
114
50
115
- return w
51
+ return failures
52
+ }
53
+
54
+ // Name returns the rule name.
55
+ func (* ModifiesValRecRule ) Name () string {
56
+ return "modifies-value-receiver"
116
57
}
117
58
118
- func (w lintModifiesValRecRule ) skipType (t ast.Expr ) bool {
119
- rt := w . file . Pkg .TypeOf (t )
59
+ func (r * ModifiesValRecRule ) skipType (t ast.Expr , pkg * lint. Package ) bool {
60
+ rt := pkg .TypeOf (t )
120
61
if rt == nil {
121
62
return false
122
63
}
@@ -128,7 +69,7 @@ func (w lintModifiesValRecRule) skipType(t ast.Expr) bool {
128
69
return strings .HasPrefix (rtName , "[]" ) || strings .HasPrefix (rtName , "map[" )
129
70
}
130
71
131
- func (lintModifiesValRecRule ) getNameFromExpr (ie ast.Expr ) string {
72
+ func (* ModifiesValRecRule ) getNameFromExpr (ie ast.Expr ) string {
132
73
ident , ok := ie .(* ast.Ident )
133
74
if ! ok {
134
75
return ""
@@ -137,7 +78,7 @@ func (lintModifiesValRecRule) getNameFromExpr(ie ast.Expr) string {
137
78
return ident .Name
138
79
}
139
80
140
- func (w lintModifiesValRecRule ) findReturnReceiverStatements (receiverName string , target ast.Node ) []ast.Node {
81
+ func (r * ModifiesValRecRule ) findReturnReceiverStatements (receiverName string , target ast.Node ) []ast.Node {
141
82
finder := func (n ast.Node ) bool {
142
83
// look for returns with the receiver as value
143
84
returnStatement , ok := n .(* ast.ReturnStmt )
@@ -148,7 +89,7 @@ func (w lintModifiesValRecRule) findReturnReceiverStatements(receiverName string
148
89
for _ , exp := range returnStatement .Results {
149
90
switch e := exp .(type ) {
150
91
case * ast.SelectorExpr : // receiver.field = ...
151
- name := w .getNameFromExpr (e .X )
92
+ name := r .getNameFromExpr (e .X )
152
93
if name == "" || name != receiverName {
153
94
continue
154
95
}
@@ -160,7 +101,7 @@ func (w lintModifiesValRecRule) findReturnReceiverStatements(receiverName string
160
101
if e .Op != token .AND {
161
102
continue
162
103
}
163
- name := w .getNameFromExpr (e .X )
104
+ name := r .getNameFromExpr (e .X )
164
105
if name == "" || name != receiverName {
165
106
continue
166
107
}
@@ -177,3 +118,60 @@ func (w lintModifiesValRecRule) findReturnReceiverStatements(receiverName string
177
118
178
119
return pick (target , finder )
179
120
}
121
+
122
+ func (r * ModifiesValRecRule ) mustSkip (receiver * ast.Field , pkg * lint.Package ) bool {
123
+ if _ , ok := receiver .Type .(* ast.StarExpr ); ok {
124
+ return true // skip, method with pointer receiver
125
+ }
126
+
127
+ if len (receiver .Names ) < 1 {
128
+ return true // skip, anonymous receiver
129
+ }
130
+
131
+ receiverName := receiver .Names [0 ].Name
132
+ if receiverName == "_" {
133
+ return true // skip, anonymous receiver
134
+ }
135
+
136
+ if r .skipType (receiver .Type , pkg ) {
137
+ return true // skip, receiver is a map or array
138
+ }
139
+
140
+ return false
141
+ }
142
+
143
+ func (r * ModifiesValRecRule ) getAssignmentsToReceiver (receiverName string , funcBody * ast.BlockStmt ) []ast.Node {
144
+ receiverAssignmentFinder := func (n ast.Node ) bool {
145
+ // look for assignments with the receiver in the right hand
146
+ assignment , ok := n .(* ast.AssignStmt )
147
+ if ! ok {
148
+ return false
149
+ }
150
+
151
+ for _ , exp := range assignment .Lhs {
152
+ switch e := exp .(type ) {
153
+ case * ast.IndexExpr : // receiver...[] = ...
154
+ continue
155
+ case * ast.StarExpr : // *receiver = ...
156
+ continue
157
+ case * ast.SelectorExpr : // receiver.field = ...
158
+ name := r .getNameFromExpr (e .X )
159
+ if name == "" || name != receiverName {
160
+ continue
161
+ }
162
+ case * ast.Ident : // receiver := ...
163
+ if e .Name != receiverName {
164
+ continue
165
+ }
166
+ default :
167
+ continue
168
+ }
169
+
170
+ return true
171
+ }
172
+
173
+ return false
174
+ }
175
+
176
+ return pick (funcBody , receiverAssignmentFinder )
177
+ }
0 commit comments