diff --git a/internal/codeguard/checks/reliability/reliability_go.go b/internal/codeguard/checks/reliability/reliability_go.go index f7a69f6..bf2cc09 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 03392e1..0b33962 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" {