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.
6 This file contains the code to check range loop variables bound inside function
7 literals that are deferred or launched in new goroutines. We only check
8 instances where the defer or go statement is the last statement in the loop
9 body, as otherwise we would need whole program analysis.
15 println(i, v) // not what you might expect
19 See: https://golang.org/doc/go_faq.html#closures_and_goroutines
27 register("rangeloops",
28 "check that loop variables are used correctly",
33 // checkLoop walks the body of the provided loop statement, checking whether
34 // its index or value variables are used unsafely inside goroutines or deferred
36 func checkLoop(f
*File
, node ast
.Node
) {
37 // Find the variables updated by the loop statement.
39 addVar
:= func(expr ast
.Expr
) {
40 if id
, ok
:= expr
.(*ast
.Ident
); ok
{
41 vars
= append(vars
, id
)
44 var body
*ast
.BlockStmt
45 switch n
:= node
.(type) {
52 switch post
:= n
.Post
.(type) {
54 // e.g. for p = head; p != nil; p = p.next
55 for _
, lhs
:= range post
.Lhs
{
59 // e.g. for i := 0; i < n; i++
67 // Inspect a go or defer statement
68 // if it's the last one in the loop body.
69 // (We give up if there are following statements,
70 // because it's hard to prove go isn't followed by wait,
71 // or defer by return.)
72 if len(body
.List
) == 0 {
75 var last
*ast
.CallExpr
76 switch s
:= body
.List
[len(body
.List
)-1].(type) {
84 lit
, ok
:= last
.Fun
.(*ast
.FuncLit
)
88 ast
.Inspect(lit
.Body
, func(n ast
.Node
) bool {
89 id
, ok
:= n
.(*ast
.Ident
)
90 if !ok || id
.Obj
== nil {
93 if f
.pkg
.types
[id
].Type
== nil {
94 // Not referring to a variable (e.g. struct field name)
97 for _
, v
:= range vars
{
99 f
.Badf(id
.Pos(), "loop variable %s captured by func literal",