From b5605841149067b58e1d7271c45f45e304dd39ee Mon Sep 17 00:00:00 2001 From: alex <53851759+alxxjohn@users.noreply.github.com> Date: Tue, 18 Aug 2026 18:48:55 -0400 Subject: [PATCH] fix: make goroutine loop detection linear --- .../checks/reliability/reliability_go.go | 18 ++++++++++++- .../reliability/reliability_go_helpers.go | 25 ------------------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/internal/codeguard/checks/reliability/reliability_go.go b/internal/codeguard/checks/reliability/reliability_go.go index f7a69f6c..bf2cc094 100644 --- a/internal/codeguard/checks/reliability/reliability_go.go +++ b/internal/codeguard/checks/reliability/reliability_go.go @@ -42,12 +42,21 @@ func functionReliabilityFindings(env support.Context, file string, fset *token.F deferCloseVars := deferredCloseVars(fn.Body) boundedClients, boundedRequests := boundedHTTPValues(fn.Body, httpAliases) goroutines := 0 + loopDepth := 0 + var loopStack []bool ast.Inspect(fn.Body, func(node ast.Node) bool { + if node == nil { + if loopStack[len(loopStack)-1] { + loopDepth-- + } + loopStack = loopStack[:len(loopStack)-1] + return true + } switch n := node.(type) { case *ast.GoStmt: goroutines++ - if enabled(rules.DetectUnboundedWork) && isInsideLoop(fn.Body, n) { + if enabled(rules.DetectUnboundedWork) && loopDepth > 0 { pos := fset.Position(n.Go) findings = append(findings, newFinding(env, "reliability.unbounded-work", "warn", file, pos.Line, pos.Column, "goroutine launched inside a loop without an obvious work bound", "high", "work", "goroutine-in-loop")) } @@ -67,6 +76,13 @@ func functionReliabilityFindings(env support.Context, file string, fset *token.F findings = append(findings, lostErrorContextFindings(env, file, fset, n)...) } } + _, isFor := node.(*ast.ForStmt) + _, isRange := node.(*ast.RangeStmt) + isLoop := isFor || isRange + loopStack = append(loopStack, isLoop) + if isLoop { + loopDepth++ + } return true }) diff --git a/internal/codeguard/checks/reliability/reliability_go_helpers.go b/internal/codeguard/checks/reliability/reliability_go_helpers.go index 03392e15..0b339628 100644 --- a/internal/codeguard/checks/reliability/reliability_go_helpers.go +++ b/internal/codeguard/checks/reliability/reliability_go_helpers.go @@ -334,31 +334,6 @@ func fileHasSelector(parsed *ast.File, selector string) bool { return found } -func isInsideLoop(root ast.Node, target ast.Node) bool { - inside := false - var stack []ast.Node - ast.Inspect(root, func(node ast.Node) bool { - if node == nil { - if len(stack) > 0 { - stack = stack[:len(stack)-1] - } - return true - } - if node == target { - for _, item := range stack { - switch item.(type) { - case *ast.ForStmt, *ast.RangeStmt: - inside = true - return false - } - } - } - stack = append(stack, node) - return true - }) - return inside -} - func isErrorsNewCall(call *ast.CallExpr) bool { selector, ok := call.Fun.(*ast.SelectorExpr) if !ok || selector.Sel.Name != "New" {