| // 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 tests for the loopclosure checker. |
| |
| package testdata |
| |
| import ( |
| "sync" |
| |
| "golang.org/x/sync/errgroup" |
| ) |
| |
| var A int |
| |
| func _() { |
| var s []int |
| for i, v := range s { |
| go func() { |
| println(i) // want "loop variable i captured by func literal" |
| println(v) // want "loop variable v captured by func literal" |
| }() |
| } |
| for i, v := range s { |
| defer func() { |
| println(i) // want "loop variable i captured by func literal" |
| println(v) // want "loop variable v captured by func literal" |
| }() |
| } |
| for i := range s { |
| go func() { |
| println(i) // want "loop variable i captured by func literal" |
| }() |
| } |
| for _, v := range s { |
| go func() { |
| println(v) // want "loop variable v captured by func literal" |
| }() |
| } |
| for i, v := range s { |
| go func() { |
| println(i, v) |
| }() |
| println("unfortunately, we don't catch the error above because of this statement") |
| } |
| for i, v := range s { |
| go func(i, v int) { |
| println(i, v) |
| }(i, v) |
| } |
| for i, v := range s { |
| i, v := i, v |
| go func() { |
| println(i, v) |
| }() |
| } |
| |
| // iteration variable declared outside the loop |
| for A = range s { |
| go func() { |
| println(A) // want "loop variable A captured by func literal" |
| }() |
| } |
| // iteration variable declared in a different file |
| for B = range s { |
| go func() { |
| println(B) // want "loop variable B captured by func literal" |
| }() |
| } |
| // If the key of the range statement is not an identifier |
| // the code should not panic (it used to). |
| var x [2]int |
| var f int |
| for x[0], f = range s { |
| go func() { |
| _ = f // want "loop variable f captured by func literal" |
| }() |
| } |
| type T struct { |
| v int |
| } |
| for _, v := range s { |
| go func() { |
| _ = T{v: 1} |
| _ = map[int]int{v: 1} // want "loop variable v captured by func literal" |
| }() |
| } |
| |
| // ordinary for-loops |
| for i := 0; i < 10; i++ { |
| go func() { |
| print(i) // want "loop variable i captured by func literal" |
| }() |
| } |
| for i, j := 0, 1; i < 100; i, j = j, i+j { |
| go func() { |
| print(j) // want "loop variable j captured by func literal" |
| }() |
| } |
| type cons struct { |
| car int |
| cdr *cons |
| } |
| var head *cons |
| for p := head; p != nil; p = p.cdr { |
| go func() { |
| print(p.car) // want "loop variable p captured by func literal" |
| }() |
| } |
| } |
| |
| // Cases that rely on recursively checking for last statements. |
| func _() { |
| |
| for i := range "outer" { |
| for j := range "inner" { |
| if j < 1 { |
| defer func() { |
| print(i) // want "loop variable i captured by func literal" |
| }() |
| } else if j < 2 { |
| go func() { |
| print(i) // want "loop variable i captured by func literal" |
| }() |
| } else { |
| go func() { |
| print(i) |
| }() |
| println("we don't catch the error above because of this statement") |
| } |
| } |
| } |
| |
| for i := 0; i < 10; i++ { |
| for j := 0; j < 10; j++ { |
| if j < 1 { |
| switch j { |
| case 0: |
| defer func() { |
| print(i) // want "loop variable i captured by func literal" |
| }() |
| default: |
| go func() { |
| print(i) // want "loop variable i captured by func literal" |
| }() |
| } |
| } else if j < 2 { |
| var a interface{} = j |
| switch a.(type) { |
| case int: |
| defer func() { |
| print(i) // want "loop variable i captured by func literal" |
| }() |
| default: |
| go func() { |
| print(i) // want "loop variable i captured by func literal" |
| }() |
| } |
| } else { |
| ch := make(chan string) |
| select { |
| case <-ch: |
| defer func() { |
| print(i) // want "loop variable i captured by func literal" |
| }() |
| default: |
| go func() { |
| print(i) // want "loop variable i captured by func literal" |
| }() |
| } |
| } |
| } |
| } |
| } |
| |
| // Group is used to test that loopclosure only matches Group.Go when Group is |
| // from the golang.org/x/sync/errgroup package. |
| type Group struct{} |
| |
| func (g *Group) Go(func() error) {} |
| |
| func _() { |
| var s []int |
| // errgroup.Group.Go() invokes Go routines |
| g := new(errgroup.Group) |
| for i, v := range s { |
| g.Go(func() error { |
| print(i) // want "loop variable i captured by func literal" |
| print(v) // want "loop variable v captured by func literal" |
| return nil |
| }) |
| } |
| |
| for i, v := range s { |
| if i > 0 { |
| g.Go(func() error { |
| print(i) // want "loop variable i captured by func literal" |
| return nil |
| }) |
| } else { |
| g.Go(func() error { |
| print(v) // want "loop variable v captured by func literal" |
| return nil |
| }) |
| } |
| } |
| |
| // Do not match other Group.Go cases |
| g1 := new(Group) |
| for i, v := range s { |
| g1.Go(func() error { |
| print(i) |
| print(v) |
| return nil |
| }) |
| } |
| } |
| |
| // Real-world example from #16520, slightly simplified |
| func _() { |
| var nodes []interface{} |
| |
| critical := new(errgroup.Group) |
| others := sync.WaitGroup{} |
| |
| isCritical := func(node interface{}) bool { return false } |
| run := func(node interface{}) error { return nil } |
| |
| for _, node := range nodes { |
| if isCritical(node) { |
| critical.Go(func() error { |
| return run(node) // want "loop variable node captured by func literal" |
| }) |
| } else { |
| others.Add(1) |
| go func() { |
| _ = run(node) // want "loop variable node captured by func literal" |
| others.Done() |
| }() |
| } |
| } |
| } |