PR tree-optimization/86274 - SEGFAULT when logging std::to_string(NAN)
[official-gcc.git] / libgo / go / cmd / vet / rangeloop.go
blob53a41364dfe1b84e79d2d0bdfa4ac25694e7b29b
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.
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.
11 For example:
13 for i, v := range s {
14 go func() {
15 println(i, v) // not what you might expect
16 }()
19 See: https://golang.org/doc/go_faq.html#closures_and_goroutines
22 package main
24 import "go/ast"
26 func init() {
27 register("rangeloops",
28 "check that loop variables are used correctly",
29 checkLoop,
30 rangeStmt, forStmt)
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
35 // function literals.
36 func checkLoop(f *File, node ast.Node) {
37 // Find the variables updated by the loop statement.
38 var vars []*ast.Ident
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) {
46 case *ast.RangeStmt:
47 body = n.Body
48 addVar(n.Key)
49 addVar(n.Value)
50 case *ast.ForStmt:
51 body = n.Body
52 switch post := n.Post.(type) {
53 case *ast.AssignStmt:
54 // e.g. for p = head; p != nil; p = p.next
55 for _, lhs := range post.Lhs {
56 addVar(lhs)
58 case *ast.IncDecStmt:
59 // e.g. for i := 0; i < n; i++
60 addVar(post.X)
63 if vars == nil {
64 return
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 {
73 return
75 var last *ast.CallExpr
76 switch s := body.List[len(body.List)-1].(type) {
77 case *ast.GoStmt:
78 last = s.Call
79 case *ast.DeferStmt:
80 last = s.Call
81 default:
82 return
84 lit, ok := last.Fun.(*ast.FuncLit)
85 if !ok {
86 return
88 ast.Inspect(lit.Body, func(n ast.Node) bool {
89 id, ok := n.(*ast.Ident)
90 if !ok || id.Obj == nil {
91 return true
93 if f.pkg.types[id].Type == nil {
94 // Not referring to a variable (e.g. struct field name)
95 return true
97 for _, v := range vars {
98 if v.Obj == id.Obj {
99 f.Badf(id.Pos(), "loop variable %s captured by func literal",
100 id.Name)
103 return true