Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / analysis / passes / loopclosure / loopclosure.go
1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package loopclosure defines an Analyzer that checks for references to
6 // enclosing loop variables from within nested functions.
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 // TODO(adonovan): also report an error for the following structure,
18 // which is often used to ensure that deferred calls do not accumulate
19 // in a loop:
20 //
21 //      for i, x := range c {
22 //              func() {
23 //                      ...reference to i or x...
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                 // Find the variables updated by the loop statement.
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                                 // e.g. for p = head; p != nil; p = p.next
78                                 for _, lhs := range post.Lhs {
79                                         addVar(lhs)
80                                 }
81                         case *ast.IncDecStmt:
82                                 // e.g. for i := 0; i < n; i++
83                                 addVar(post.X)
84                         }
85                 }
86                 if vars == nil {
87                         return
88                 }
89
90                 // Inspect a go or defer statement
91                 // if it's the last one in the loop body.
92                 // (We give up if there are following statements,
93                 // because it's hard to prove go isn't followed by wait,
94                 // or defer by return.)
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                                 // Not referring to a variable (e.g. struct field name)
118                                 return true
119                         }
120                         for _, v := range vars {
121                                 if v.Obj == id.Obj {
122                                         pass.ReportRangef(id, "loop variable %s captured by func literal",
123                                                 id.Name)
124                                 }
125                         }
126                         return true
127                 })
128         })
129         return nil, nil
130 }