go/analysis/passes/modernize: fix the potential deadlock issues in bloop

For golang/go#75599

Change-Id: Ic8009f9a7bcbd8f76b47bf6831132aeb0cbee2f3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/711980
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Bypass: Alan Donovan <adonovan@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
diff --git a/go/analysis/passes/modernize/bloop.go b/go/analysis/passes/modernize/bloop.go
index eb1ac17..bb8cf72 100644
--- a/go/analysis/passes/modernize/bloop.go
+++ b/go/analysis/passes/modernize/bloop.go
@@ -189,6 +189,7 @@
 // 2. The only b.N loop in that benchmark function
 //   - b.Loop() can only be called once per benchmark execution
 //   - Multiple calls result in "B.Loop called with timer stopped" error
+//   - Multiple loops may have complex interdependencies that are hard to analyze
 func usesBenchmarkNOnce(c inspector.Cursor, info *types.Info) bool {
 	// Find the enclosing benchmark function
 	curFunc, ok := enclosingFunc(c)
@@ -205,17 +206,14 @@
 		return false
 	}
 
-	// Count b.N references in this benchmark function
+	// Count all b.N references in this benchmark function (including nested functions)
 	bnRefCount := 0
-	filter := []ast.Node{(*ast.SelectorExpr)(nil), (*ast.FuncLit)(nil)}
+	filter := []ast.Node{(*ast.SelectorExpr)(nil)}
 	curFunc.Inspect(filter, func(cur inspector.Cursor) bool {
-		switch n := cur.Node().(type) {
-		case *ast.FuncLit:
-			return false // don't descend into nested function literals
-		case *ast.SelectorExpr:
-			if n.Sel.Name == "N" && typesinternal.IsPointerToNamed(info.TypeOf(n.X), "testing", "B") {
-				bnRefCount++
-			}
+		sel := cur.Node().(*ast.SelectorExpr)
+		if sel.Sel.Name == "N" &&
+			typesinternal.IsPointerToNamed(info.TypeOf(sel.X), "testing", "B") {
+			bnRefCount++
 		}
 		return true
 	})
diff --git a/go/analysis/passes/modernize/testdata/src/bloop/bloop_test.go b/go/analysis/passes/modernize/testdata/src/bloop/bloop_test.go
index 2dc71fc..33ffd77 100644
--- a/go/analysis/passes/modernize/testdata/src/bloop/bloop_test.go
+++ b/go/analysis/passes/modernize/testdata/src/bloop/bloop_test.go
@@ -113,3 +113,21 @@
 	for i := 0; i < b.N; i++ { // nope: b.N accessed more than once in benchmark
 	}
 }
+
+func BenchmarkJ(b *testing.B) {
+	var wg sync.WaitGroup
+	ch := make(chan int, 10)
+	wg.Add(1)
+	go func() {
+		for i := 0; i < b.N; i++ {
+			<-ch
+		}
+		wg.Done()
+	}()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ { // nope: multiple b.N loops in same function
+		ch <- i
+	}
+	b.StopTimer()
+	wg.Wait()
+}
diff --git a/go/analysis/passes/modernize/testdata/src/bloop/bloop_test.go.golden b/go/analysis/passes/modernize/testdata/src/bloop/bloop_test.go.golden
index 3def4a0..a2def76 100644
--- a/go/analysis/passes/modernize/testdata/src/bloop/bloop_test.go.golden
+++ b/go/analysis/passes/modernize/testdata/src/bloop/bloop_test.go.golden
@@ -111,3 +111,21 @@
 	for i := 0; i < b.N; i++ { // nope: b.N accessed more than once in benchmark
 	}
 }
+
+func BenchmarkJ(b *testing.B) {
+	var wg sync.WaitGroup
+	ch := make(chan int, 10)
+	wg.Add(1)
+	go func() {
+		for i := 0; i < b.N; i++ {
+			<-ch
+		}
+		wg.Done()
+	}()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ { // nope: multiple b.N loops in same function
+		ch <- i
+	}
+	b.StopTimer()
+	wg.Wait()
+}