1 // Copyright 2023 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 // 8 // # Analyzer loopclosure 9 // 10 // loopclosure: check references to loop variables from within nested functions 11 // 12 // This analyzer reports places where a function literal references the 13 // iteration variable of an enclosing loop, and the loop calls the function 14 // in such a way (e.g. with go or defer) that it may outlive the loop 15 // iteration and possibly observe the wrong value of the variable. 16 // 17 // Note: An iteration variable can only outlive a loop iteration in Go versions <=1.21. 18 // In Go 1.22 and later, the loop variable lifetimes changed to create a new 19 // iteration variable per loop iteration. (See go.dev/issue/60078.) 20 // 21 // In this example, all the deferred functions run after the loop has 22 // completed, so all observe the final value of v [<go1.22]. 23 // 24 // for _, v := range list { 25 // defer func() { 26 // use(v) // incorrect 27 // }() 28 // } 29 // 30 // One fix is to create a new variable for each iteration of the loop: 31 // 32 // for _, v := range list { 33 // v := v // new var per iteration 34 // defer func() { 35 // use(v) // ok 36 // }() 37 // } 38 // 39 // After Go version 1.22, the previous two for loops are equivalent 40 // and both are correct. 41 // 42 // The next example uses a go statement and has a similar problem [<go1.22]. 43 // In addition, it has a data race because the loop updates v 44 // concurrent with the goroutines accessing it. 45 // 46 // for _, v := range elem { 47 // go func() { 48 // use(v) // incorrect, and a data race 49 // }() 50 // } 51 // 52 // A fix is the same as before. The checker also reports problems 53 // in goroutines started by golang.org/x/sync/errgroup.Group. 54 // A hard-to-spot variant of this form is common in parallel tests: 55 // 56 // func Test(t *testing.T) { 57 // for _, test := range tests { 58 // t.Run(test.name, func(t *testing.T) { 59 // t.Parallel() 60 // use(test) // incorrect, and a data race 61 // }) 62 // } 63 // } 64 // 65 // The t.Parallel() call causes the rest of the function to execute 66 // concurrent with the loop [<go1.22]. 67 // 68 // The analyzer reports references only in the last statement, 69 // as it is not deep enough to understand the effects of subsequent 70 // statements that might render the reference benign. 71 // ("Last statement" is defined recursively in compound 72 // statements such as if, switch, and select.) 73 // 74 // See: https://golang.org/doc/go_faq.html#closures_and_goroutines 75 package loopclosure 76