Skip to content

Commit bb96292

Browse files
committed
Suggest fixes
fix: #7
1 parent 6793c6b commit bb96292

File tree

1 file changed

+52
-25
lines changed

1 file changed

+52
-25
lines changed

exportloopref.go

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,28 @@ func run(pass *analysis.Pass) (interface{}, error) {
4343
}
4444

4545
inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool {
46-
id, digg := search.Check(n, stack)
46+
id, insert, digg := search.Check(n, stack)
4747
if id != nil {
48-
msg := fmt.Sprintf("exporting a pointer for the loop variable %s", id.Name)
49-
pass.Report(analysis.Diagnostic{Pos: id.Pos(), End: id.End(), Message: msg, Category: "exportloopref"})
48+
dMsg := fmt.Sprintf("exporting a pointer for the loop variable %s", id.Name)
49+
fMsg := fmt.Sprintf("loop variable %s should be pinned", id.Name)
50+
var suggest []analysis.SuggestedFix
51+
if insert != token.NoPos {
52+
suggest = []analysis.SuggestedFix{{
53+
Message: fMsg,
54+
TextEdits: []analysis.TextEdit{{
55+
Pos: insert,
56+
End: insert,
57+
NewText: []byte(fmt.Sprintf("%[1]s := %[1]s\n", id.Name)),
58+
}},
59+
}}
60+
}
61+
d := analysis.Diagnostic{Pos: id.Pos(),
62+
End: id.End(),
63+
Message: dMsg,
64+
Category: "exportloopref",
65+
SuggestedFixes: suggest,
66+
}
67+
pass.Report(d)
5068
}
5169
return digg
5270
})
@@ -67,7 +85,7 @@ type Searcher struct {
6785
Types map[ast.Expr]types.TypeAndValue
6886
}
6987

70-
func (s *Searcher) Check(n ast.Node, stack []ast.Node) (*ast.Ident, bool) {
88+
func (s *Searcher) Check(n ast.Node, stack []ast.Node) (*ast.Ident, token.Pos, bool) {
7189
switch typed := n.(type) {
7290
case *ast.RangeStmt:
7391
s.parseRangeStmt(typed)
@@ -81,7 +99,7 @@ func (s *Searcher) Check(n ast.Node, stack []ast.Node) (*ast.Ident, bool) {
8199
case *ast.UnaryExpr:
82100
return s.checkUnaryExpr(typed, stack)
83101
}
84-
return nil, true
102+
return nil, token.NoPos, true
85103
}
86104

87105
func (s *Searcher) parseRangeStmt(n *ast.RangeStmt) {
@@ -109,7 +127,7 @@ func (s *Searcher) addStat(expr ast.Expr) {
109127
}
110128

111129
func (s *Searcher) parseDeclStmt(n *ast.DeclStmt, stack []ast.Node) {
112-
loop := s.innermostLoop(stack)
130+
loop, _ := s.innermostLoop(stack)
113131
if loop == nil {
114132
return
115133
}
@@ -125,7 +143,7 @@ func (s *Searcher) parseDeclStmt(n *ast.DeclStmt, stack []ast.Node) {
125143
}
126144

127145
func (s *Searcher) parseAssignStmt(n *ast.AssignStmt, stack []ast.Node) {
128-
loop := s.innermostLoop(stack)
146+
loop, _ := s.innermostLoop(stack)
129147
if loop == nil {
130148
return
131149
}
@@ -152,36 +170,45 @@ func (s *Searcher) addVar(loop ast.Node, expr ast.Expr) {
152170
s.Vars[loopPos] = vars
153171
}
154172

155-
func (s *Searcher) innermostLoop(stack []ast.Node) ast.Node {
173+
func insertionPosition(block *ast.BlockStmt) token.Pos {
174+
if len(block.List) > 0 {
175+
return block.List[0].Pos()
176+
}
177+
return token.NoPos
178+
}
179+
180+
func (s *Searcher) innermostLoop(stack []ast.Node) (ast.Node, token.Pos) {
156181
for i := len(stack) - 1; i >= 0; i-- {
157-
switch stack[i].(type) {
158-
case *ast.RangeStmt, *ast.ForStmt:
159-
return stack[i]
182+
switch typed := stack[i].(type) {
183+
case *ast.RangeStmt:
184+
return typed, insertionPosition(typed.Body)
185+
case *ast.ForStmt:
186+
return typed, insertionPosition(typed.Body)
160187
}
161188
}
162-
return nil
189+
return nil, token.NoPos
163190
}
164191

165-
func (s *Searcher) checkUnaryExpr(n *ast.UnaryExpr, stack []ast.Node) (*ast.Ident, bool) {
166-
loop := s.innermostLoop(stack)
192+
func (s *Searcher) checkUnaryExpr(n *ast.UnaryExpr, stack []ast.Node) (*ast.Ident, token.Pos, bool) {
193+
loop, insert := s.innermostLoop(stack)
167194
if loop == nil {
168-
return nil, true
195+
return nil, token.NoPos, true
169196
}
170197

171198
if n.Op != token.AND {
172-
return nil, true
199+
return nil, token.NoPos, true
173200
}
174201

175202
// Get identity of the referred item
176203
id := s.getIdentity(n.X)
177204
if id == nil {
178-
return nil, true
205+
return nil, token.NoPos, true
179206
}
180207

181208
// If the identity is not the loop statement variable,
182209
// it will not be reported.
183210
if _, isStat := s.Stats[id.Obj.Pos()]; !isStat {
184-
return nil, true
211+
return nil, token.NoPos, true
185212
}
186213

187214
// check stack append(), []X{}, map[Type]X{}, Struct{}, &Struct{}, X.(Type), (X)
@@ -198,16 +225,16 @@ func (s *Searcher) checkUnaryExpr(n *ast.UnaryExpr, stack []ast.Node) (*ast.Iden
198225
case (*ast.CallExpr):
199226
fun, ok := typed.Fun.(*ast.Ident)
200227
if !ok {
201-
return nil, false // it's calling a function other of `append`. It cannot be checked
228+
return nil, token.NoPos, false // it's calling a function other of `append`. It cannot be checked
202229
}
203230

204231
if fun.Name != "append" {
205-
return nil, false // it's calling a function other of `append`. It cannot be checked
232+
return nil, token.NoPos, false // it's calling a function other of `append`. It cannot be checked
206233
}
207234

208235
case (*ast.AssignStmt):
209236
if len(typed.Rhs) != len(typed.Lhs) {
210-
return nil, false // dead logic
237+
return nil, token.NoPos, false // dead logic
211238
}
212239

213240
// search x where Rhs[x].Pos() == mayRHPos
@@ -223,19 +250,19 @@ func (s *Searcher) checkUnaryExpr(n *ast.UnaryExpr, stack []ast.Node) (*ast.Iden
223250
lh := typed.Lhs[index]
224251
isVar := s.isVar(loop, lh)
225252
if !isVar {
226-
return id, false
253+
return id, insert, false
227254
}
228255

229-
return nil, true
256+
return nil, token.NoPos, true
230257
default:
231258
// Other statement is not able to be checked.
232-
return nil, false
259+
return nil, token.NoPos, false
233260
}
234261

235262
// memory an expr that may be right-hand in the AssignStmt
236263
mayRHPos = stack[i].Pos()
237264
}
238-
return nil, true
265+
return nil, token.NoPos, true
239266
}
240267

241268
func (s *Searcher) isVar(loop ast.Node, expr ast.Expr) bool {

0 commit comments

Comments
 (0)