github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/vet/rangeloop.go (about) 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 /* 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. 10 11 For example: 12 13 for i, v := range s { 14 go func() { 15 println(i, v) // not what you might expect 16 }() 17 } 18 19 See: http://golang.org/doc/go_faq.html#closures_and_goroutines 20 */ 21 22 package main 23 24 import "go/ast" 25 26 // checkRangeLoop walks the body of the provided range statement, checking if 27 // its index or value variables are used unsafely inside goroutines or deferred 28 // function literals. 29 func checkRangeLoop(f *File, n *ast.RangeStmt) { 30 if !vet("rangeloops") { 31 return 32 } 33 key, _ := n.Key.(*ast.Ident) 34 val, _ := n.Value.(*ast.Ident) 35 if key == nil && val == nil { 36 return 37 } 38 sl := n.Body.List 39 if len(sl) == 0 { 40 return 41 } 42 var last *ast.CallExpr 43 switch s := sl[len(sl)-1].(type) { 44 case *ast.GoStmt: 45 last = s.Call 46 case *ast.DeferStmt: 47 last = s.Call 48 default: 49 return 50 } 51 lit, ok := last.Fun.(*ast.FuncLit) 52 if !ok { 53 return 54 } 55 ast.Inspect(lit.Body, func(n ast.Node) bool { 56 id, ok := n.(*ast.Ident) 57 if !ok || id.Obj == nil { 58 return true 59 } 60 if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj { 61 f.Warn(id.Pos(), "range variable", id.Name, "enclosed by function") 62 } 63 return true 64 }) 65 }