rastray 0.15.0

Blazing-fast static analysis CLI for security, dependency, and performance audits.
# RSTR-PERF-301 — `defer` inside a `for` loop

## Summary

Go's `defer` runs at function return, not loop iteration. A `defer`
inside a `for` body accumulates on the function's defer stack —
every iteration pushes a new entry, and nothing pops them until the
enclosing function returns. For long-running loops over file handles
or DB rows, that means resources stay held for the whole loop, not
just the iteration that opened them.

## Severity

`Medium`. Resource exhaustion (file descriptors, DB connections) is
the usual failure mode.

## Languages

Go.

## What rastray flags

```go
for _, path := range paths {
    f, err := os.Open(path)
    if err != nil { return err }
    defer f.Close()                       // ← flagged
    ...
}
```

## What rastray deliberately does *not* flag

- `defer` at function scope.
- `defer` inside an immediately-invoked closure inside the loop
  (which scopes the defer to the closure, not the function).

## How to fix it

**Option 1** — wrap the loop body in a closure (or named function)
so `defer` fires per iteration:

```go
for _, path := range paths {
    if err := func() error {
        f, err := os.Open(path)
        if err != nil { return err }
        defer f.Close()
        return process(f)
    }(); err != nil {
        return err
    }
}
```

**Option 2** — close explicitly inside the loop:

```go
for _, path := range paths {
    f, err := os.Open(path)
    if err != nil { return err }
    err = process(f)
    f.Close()
    if err != nil { return err }
}
```

The closure form is more idiomatic for clean-up that involves
multiple `defer`s (close + unlock + commit).

## References

- [Go blog: defer, panic, and recover]https://go.dev/blog/defer-panic-and-recover
- [Effective Go: defer]https://go.dev/doc/effective_go#defer