deferinloop
Checks cleanup defers whose lifetime extends across loop iterations.
What it detects
A defer inside a loop runs when the surrounding function returns, not when the iteration ends. Put cleanup-sensitive work in a helper function so files, locks, timers, and similar resources are released after each iteration.
The full list of precision boundaries, with the fixtures that pin each one, is in the deferinloop design notes.
Checks
| Check | Tier | Kind | What it detects |
|---|---|---|---|
cleanup-lifetime |
core | hazard | Reports cleanup defers whose lifetime extends across loop iterations. |
Why this is flagged
Deferring cleanup until the whole function returns lets resources accumulate across iterations. A large or long-running loop can then exhaust file handles, hold locks too long, or keep timers and connections alive unnecessarily.
A resource stored in a collection that outlives the loop is not reported, because the collection may keep it on purpose.
Further reading: Effective Go: Defer.
How to fix it
Move the body of one iteration into a small helper function and defer cleanup there. The helper returns at the end of each iteration, so its resources are released before the next iteration starts.
Examples
Flagged code
func readAll(names []string) error { for _, name := range names { file, err := os.Open(name) if err != nil { return err } // gohawk: deferred cleanup runs after the loop instead of after this iteration defer file.Close() } return nil}Accepted code
func readAllSafely(names []string) error { for _, name := range names { if err := readOne(name); err != nil { return err } } return nil}
func readOne(name string) error { file, err := os.Open(name) if err != nil { return err } defer file.Close() return nil}