Skip to content

Commit 1fb9ccd

Browse files
committed
fix comments and order of the fast-return
commit 7b348bb Author: Kyoichiro Yamada <[email protected]> Date: Sat Jul 31 10:34:16 2021 +0900 fix fast return commit 3e8fccf Author: Kyoichiro Yamada <[email protected]> Date: Sat Jul 31 10:16:19 2021 +0900 fix comments and fast-return commit a1015f4 Author: Kyoichiro Yamada <[email protected]> Date: Sat Jul 31 09:17:09 2021 +0900 trim comment
1 parent ed0fedd commit 1fb9ccd

File tree

1 file changed

+96
-70
lines changed

1 file changed

+96
-70
lines changed

exportloopref.go

Lines changed: 96 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,15 @@ var Analyzer = &analysis.Analyzer{
1717
Run: run,
1818
RunDespiteErrors: true,
1919
Requires: []*analysis.Analyzer{inspect.Analyzer},
20-
// ResultType reflect.Type
21-
// FactTypes []Fact
22-
}
23-
24-
func init() {
25-
// Analyzer.Flags.StringVar(&v, "name", "default", "description")
2620
}
2721

2822
func run(pass *analysis.Pass) (interface{}, error) {
2923
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
3024

3125
search := &Searcher{
32-
Stats: map[token.Pos]struct{}{},
33-
Vars: map[token.Pos]map[token.Pos]struct{}{},
34-
Types: pass.TypesInfo.Types,
26+
LoopVars: map[token.Pos]struct{}{},
27+
LocalVars: map[token.Pos]map[token.Pos]struct{}{},
28+
Pass: pass,
3529
}
3630

3731
nodeFilter := []ast.Node{
@@ -42,50 +36,60 @@ func run(pass *analysis.Pass) (interface{}, error) {
4236
(*ast.UnaryExpr)(nil),
4337
}
4438

45-
inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool {
46-
id, insert, digg := search.Check(n, stack)
47-
if id != nil {
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)
68-
}
69-
return digg
70-
})
39+
inspect.WithStack(nodeFilter, search.CheckAndReport)
7140

7241
return nil, nil
7342
}
7443

7544
type Searcher struct {
76-
// Statement variables : map to collect positions that
77-
// variables are declared like below.
45+
// LoopVars is positions that loop-variables are declared like below.
7846
// - for <KEY>, <VALUE> := range ...
79-
// - var <X> int
80-
// - D := ...
81-
Stats map[token.Pos]struct{}
82-
// Local variables maps loop-position, decl-location to ignore
83-
// safe pointers for variable which declared in the loop.
84-
Vars map[token.Pos]map[token.Pos]struct{}
85-
Types map[ast.Expr]types.TypeAndValue
47+
// - for <VALUE> := <INIT>; <CONDITION>; <INCREMENT>
48+
LoopVars map[token.Pos]struct{}
49+
// LocalVars is positions of loops and the variables declared in them.
50+
// Use this to determine if a point assignment is an export outside the loop.
51+
LocalVars map[token.Pos]map[token.Pos]struct{}
52+
53+
Pass *analysis.Pass
8654
}
8755

88-
func (s *Searcher) Check(n ast.Node, stack []ast.Node) (*ast.Ident, token.Pos, bool) {
56+
// CheckAndReport inspects each node with stack.
57+
// It is implemented as the I/F of the "golang.org/x/tools/go/analysis/passes/inspect".Analysis.WithStack.
58+
func (s *Searcher) CheckAndReport(n ast.Node, push bool, stack []ast.Node) bool {
59+
id, insert, digg := s.Check(n, stack)
60+
if id == nil {
61+
// no prob.
62+
return digg
63+
}
64+
65+
// suggests fix
66+
var suggest []analysis.SuggestedFix
67+
if insert != token.NoPos {
68+
suggest = []analysis.SuggestedFix{{
69+
Message: fmt.Sprintf("loop variable %s should be pinned", id.Name),
70+
TextEdits: []analysis.TextEdit{{
71+
Pos: insert,
72+
End: insert,
73+
NewText: []byte(fmt.Sprintf("%[1]s := %[1]s\n", id.Name)),
74+
}},
75+
}}
76+
}
77+
78+
// report a diagnostic
79+
d := analysis.Diagnostic{Pos: id.Pos(),
80+
End: id.End(),
81+
Message: fmt.Sprintf("exporting a pointer for the loop variable %s", id.Name),
82+
Category: "exportloopref",
83+
SuggestedFixes: suggest,
84+
}
85+
s.Pass.Report(d)
86+
return digg
87+
}
88+
89+
// Check each node and stack, whether it exports loop variables or not.
90+
// Finding export, report the *ast.Ident of exported loop variable,
91+
// and token.Pos to insert assignment to fix the diagnostic.
92+
func (s *Searcher) Check(n ast.Node, stack []ast.Node) (loopVar *ast.Ident, insertPos token.Pos, digg bool) {
8993
switch typed := n.(type) {
9094
case *ast.RangeStmt:
9195
s.parseRangeStmt(typed)
@@ -102,72 +106,92 @@ func (s *Searcher) Check(n ast.Node, stack []ast.Node) (*ast.Ident, token.Pos, b
102106
return nil, token.NoPos, true
103107
}
104108

109+
// parseRangeStmt will check range statement (i.e. `for <KEY>, <VALUE> := range ...`),
110+
// and collect positions of <KEY> and <VALUE>.
105111
func (s *Searcher) parseRangeStmt(n *ast.RangeStmt) {
106-
s.addStat(n.Key)
107-
s.addStat(n.Value)
112+
s.storeLoopVars(n.Key)
113+
s.storeLoopVars(n.Value)
108114
}
109115

116+
// parseForStmt will check for statement (i.e. `for <VALUE> := <INIT>; <CONDITION>; <INCREMENT>`),
117+
// and collect positions of <VALUE>.
110118
func (s *Searcher) parseForStmt(n *ast.ForStmt) {
111119
switch post := n.Post.(type) {
112120
case *ast.AssignStmt:
113121
// e.g. for p = head; p != nil; p = p.next
114122
for _, lhs := range post.Lhs {
115-
s.addStat(lhs)
123+
s.storeLoopVars(lhs)
116124
}
117125
case *ast.IncDecStmt:
118126
// e.g. for i := 0; i < n; i++
119-
s.addStat(post.X)
127+
s.storeLoopVars(post.X)
120128
}
121129
}
122130

123-
func (s *Searcher) addStat(expr ast.Expr) {
131+
func (s *Searcher) storeLoopVars(expr ast.Expr) {
124132
if id, ok := expr.(*ast.Ident); ok {
125-
s.Stats[id.Pos()] = struct{}{}
133+
s.LoopVars[id.Pos()] = struct{}{}
126134
}
127135
}
128136

137+
// parseDeclStmt will parse declaring statement (i.e. `var`, `type`, `const`),
138+
// and store the position if it is "var" declaration and is in any loop.
129139
func (s *Searcher) parseDeclStmt(n *ast.DeclStmt, stack []ast.Node) {
140+
genDecl, ok := n.Decl.(*ast.GenDecl)
141+
if !ok {
142+
// (dead branch)
143+
// if the Decl is not GenDecl (i.e. `var`, `type` or `const` statement), it is ignored
144+
return
145+
}
146+
if genDecl.Tok != token.VAR {
147+
// if the Decl is not `var` (may be `type` or `const`), it is ignored
148+
return
149+
}
150+
130151
loop, _ := s.innermostLoop(stack)
131152
if loop == nil {
132153
return
133154
}
134155

135-
// Register declaring variables
136-
if genDecl, ok := n.Decl.(*ast.GenDecl); ok && genDecl.Tok == token.VAR {
137-
for _, spec := range genDecl.Specs {
138-
for _, name := range spec.(*ast.ValueSpec).Names {
139-
s.addVar(loop, name)
140-
}
156+
// Register declared variables
157+
for _, spec := range genDecl.Specs {
158+
for _, name := range spec.(*ast.ValueSpec).Names {
159+
s.storeLocalVar(loop, name)
141160
}
142161
}
143162
}
144163

164+
// parseDeclStmt will parse assignment statement (i.e. `<VAR> = <VALUE>`),
165+
// and store the position if it is .
145166
func (s *Searcher) parseAssignStmt(n *ast.AssignStmt, stack []ast.Node) {
167+
if n.Tok != token.DEFINE {
168+
// if the statement is simple assignment (without definement), it is ignored
169+
return
170+
}
171+
146172
loop, _ := s.innermostLoop(stack)
147173
if loop == nil {
148174
return
149175
}
150176

151177
// Find statements declaring local variable
152-
if n.Tok == token.DEFINE {
153-
for _, h := range n.Lhs {
154-
s.addVar(loop, h)
155-
}
178+
for _, h := range n.Lhs {
179+
s.storeLocalVar(loop, h)
156180
}
157181
}
158182

159-
func (s *Searcher) addVar(loop ast.Node, expr ast.Expr) {
183+
func (s *Searcher) storeLocalVar(loop ast.Node, expr ast.Expr) {
160184
loopPos := loop.Pos()
161185
id, ok := expr.(*ast.Ident)
162186
if !ok {
163187
return
164188
}
165-
vars, ok := s.Vars[loopPos]
189+
vars, ok := s.LocalVars[loopPos]
166190
if !ok {
167191
vars = map[token.Pos]struct{}{}
168192
}
169193
vars[id.Obj.Pos()] = struct{}{}
170-
s.Vars[loopPos] = vars
194+
s.LocalVars[loopPos] = vars
171195
}
172196

173197
func insertionPosition(block *ast.BlockStmt) token.Pos {
@@ -189,13 +213,15 @@ func (s *Searcher) innermostLoop(stack []ast.Node) (ast.Node, token.Pos) {
189213
return nil, token.NoPos
190214
}
191215

216+
// checkUnaryExpr check unary expression (i.e. <OPERATOR><VAR> like `-x`, `*p` or `&v`) and stack.
217+
// THIS IS THE ESSENTIAL PART OF THIS PARSER.
192218
func (s *Searcher) checkUnaryExpr(n *ast.UnaryExpr, stack []ast.Node) (*ast.Ident, token.Pos, bool) {
193-
loop, insert := s.innermostLoop(stack)
194-
if loop == nil {
219+
if n.Op != token.AND {
195220
return nil, token.NoPos, true
196221
}
197222

198-
if n.Op != token.AND {
223+
loop, insert := s.innermostLoop(stack)
224+
if loop == nil {
199225
return nil, token.NoPos, true
200226
}
201227

@@ -207,7 +233,7 @@ func (s *Searcher) checkUnaryExpr(n *ast.UnaryExpr, stack []ast.Node) (*ast.Iden
207233

208234
// If the identity is not the loop statement variable,
209235
// it will not be reported.
210-
if _, isStat := s.Stats[id.Obj.Pos()]; !isStat {
236+
if _, isDecl := s.LoopVars[id.Obj.Pos()]; !isDecl {
211237
return nil, token.NoPos, true
212238
}
213239

@@ -266,7 +292,7 @@ func (s *Searcher) checkUnaryExpr(n *ast.UnaryExpr, stack []ast.Node) (*ast.Iden
266292
}
267293

268294
func (s *Searcher) isVar(loop ast.Node, expr ast.Expr) bool {
269-
vars := s.Vars[loop.Pos()] // map[token.Pos]struct{}
295+
vars := s.LocalVars[loop.Pos()] // map[token.Pos]struct{}
270296
if vars == nil {
271297
return false
272298
}
@@ -287,7 +313,7 @@ func (s *Searcher) getIdentity(expr ast.Expr) *ast.Ident {
287313
switch typed := expr.(type) {
288314
case *ast.SelectorExpr:
289315
// Ignore if the parent is pointer ref (fix for #2)
290-
if _, ok := s.Types[typed.X].Type.(*types.Pointer); ok {
316+
if _, ok := s.Pass.TypesInfo.Types[typed.X].Type.(*types.Pointer); ok {
291317
return nil
292318
}
293319

0 commit comments

Comments
 (0)