...
Source file src/pkg/cmd/vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure.go
1
2
3
4
5
6
7 package loopclosure
8
9 import (
10 "go/ast"
11
12 "golang.org/x/tools/go/analysis"
13 "golang.org/x/tools/go/analysis/passes/inspect"
14 "golang.org/x/tools/go/ast/inspector"
15 )
16
17
18
19
20
21
22
23
24
25
26
27 const Doc = `check references to loop variables from within nested functions
28
29 This analyzer checks for references to loop variables from within a
30 function literal inside the loop body. It checks only instances where
31 the function literal is called in a defer or go statement that is the
32 last statement in the loop body, as otherwise we would need whole
33 program analysis.
34
35 For example:
36
37 for i, v := range s {
38 go func() {
39 println(i, v) // not what you might expect
40 }()
41 }
42
43 See: https://golang.org/doc/go_faq.html#closures_and_goroutines`
44
45 var Analyzer = &analysis.Analyzer{
46 Name: "loopclosure",
47 Doc: Doc,
48 Requires: []*analysis.Analyzer{inspect.Analyzer},
49 Run: run,
50 }
51
52 func run(pass *analysis.Pass) (interface{}, error) {
53 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
54
55 nodeFilter := []ast.Node{
56 (*ast.RangeStmt)(nil),
57 (*ast.ForStmt)(nil),
58 }
59 inspect.Preorder(nodeFilter, func(n ast.Node) {
60
61 var vars []*ast.Ident
62 addVar := func(expr ast.Expr) {
63 if id, ok := expr.(*ast.Ident); ok {
64 vars = append(vars, id)
65 }
66 }
67 var body *ast.BlockStmt
68 switch n := n.(type) {
69 case *ast.RangeStmt:
70 body = n.Body
71 addVar(n.Key)
72 addVar(n.Value)
73 case *ast.ForStmt:
74 body = n.Body
75 switch post := n.Post.(type) {
76 case *ast.AssignStmt:
77
78 for _, lhs := range post.Lhs {
79 addVar(lhs)
80 }
81 case *ast.IncDecStmt:
82
83 addVar(post.X)
84 }
85 }
86 if vars == nil {
87 return
88 }
89
90
91
92
93
94
95 if len(body.List) == 0 {
96 return
97 }
98 var last *ast.CallExpr
99 switch s := body.List[len(body.List)-1].(type) {
100 case *ast.GoStmt:
101 last = s.Call
102 case *ast.DeferStmt:
103 last = s.Call
104 default:
105 return
106 }
107 lit, ok := last.Fun.(*ast.FuncLit)
108 if !ok {
109 return
110 }
111 ast.Inspect(lit.Body, func(n ast.Node) bool {
112 id, ok := n.(*ast.Ident)
113 if !ok || id.Obj == nil {
114 return true
115 }
116 if pass.TypesInfo.Types[id].Type == nil {
117
118 return true
119 }
120 for _, v := range vars {
121 if v.Obj == id.Obj {
122 pass.Reportf(id.Pos(), "loop variable %s captured by func literal",
123 id.Name)
124 }
125 }
126 return true
127 })
128 })
129 return nil, nil
130 }
131
View as plain text