summaryrefslogtreecommitdiff
path: root/src/cmd/vet/rangeloop.go
diff options
context:
space:
mode:
authorMichael Stapelberg <stapelberg@debian.org>2013-03-04 21:27:36 +0100
committerMichael Stapelberg <michael@stapelberg.de>2013-03-04 21:27:36 +0100
commit04b08da9af0c450d645ab7389d1467308cfc2db8 (patch)
treedb247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/cmd/vet/rangeloop.go
parent917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff)
downloadgolang-upstream/1.1_hg20130304.tar.gz
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/cmd/vet/rangeloop.go')
-rw-r--r--src/cmd/vet/rangeloop.go65
1 files changed, 65 insertions, 0 deletions
diff --git a/src/cmd/vet/rangeloop.go b/src/cmd/vet/rangeloop.go
new file mode 100644
index 000000000..ecc595427
--- /dev/null
+++ b/src/cmd/vet/rangeloop.go
@@ -0,0 +1,65 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+This file contains the code to check range loop variables bound inside function
+literals that are deferred or launched in new goroutines. We only check
+instances where the defer or go statement is the last statement in the loop
+body, as otherwise we would need whole program analysis.
+
+For example:
+
+ for i, v := range s {
+ go func() {
+ println(i, v) // not what you might expect
+ }()
+ }
+
+See: http://golang.org/doc/go_faq.html#closures_and_goroutines
+*/
+
+package main
+
+import "go/ast"
+
+// checkRangeLoop walks the body of the provided range statement, checking if
+// its index or value variables are used unsafely inside goroutines or deferred
+// function literals.
+func checkRangeLoop(f *File, n *ast.RangeStmt) {
+ if !vet("rangeloops") {
+ return
+ }
+ key, _ := n.Key.(*ast.Ident)
+ val, _ := n.Value.(*ast.Ident)
+ if key == nil && val == nil {
+ return
+ }
+ sl := n.Body.List
+ if len(sl) == 0 {
+ return
+ }
+ var last *ast.CallExpr
+ switch s := sl[len(sl)-1].(type) {
+ case *ast.GoStmt:
+ last = s.Call
+ case *ast.DeferStmt:
+ last = s.Call
+ default:
+ return
+ }
+ lit, ok := last.Fun.(*ast.FuncLit)
+ if !ok {
+ return
+ }
+ ast.Inspect(lit.Body, func(n ast.Node) bool {
+ id, ok := n.(*ast.Ident)
+ if !ok || id.Obj == nil {
+ return true
+ }
+ if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj {
+ f.Warn(id.Pos(), "range variable", id.Name, "enclosed by function")
+ }
+ return true
+ })
+}