News for hackers. Stuff that matters.
Re:Show HN: Gohawk – A Resource Leak and Concurrency Static ... by its-malbolge-ti on Thursday November 12, 2026 @01:29AM (#50463749)
Hey HN! For the past few months, I've been working really hard on a static analysis tool called gohawk, and have successfully used it to find bugs in Caddy / Docker / k8s (links in the README). It's meant to catch resource leaks and concurrency errors in Go code, such as the following example:

    func fetch(url string) ([]byte, error) {
     resp, err := http.Get(url)
     if err != nil {
      return nil, err
     }
     if resp.StatusCode != http.StatusOK {
      return nil, fmt.Errorf("unexpected status: %s", resp.Status) // resp isn't closed on all paths
     }
     defer resp.Body.Close()
     return io.ReadAll(resp.Body)
    }
I built this tool after realizing that Staticcheck and other linters don't really cover this ground yet. It works by building a symbolic heap model of the program under analysis, so that it can track object references with a high degree of accuracy. (I took this idea from Meta's Infer tool, which is developed exclusively for C++/Objective-C/Java, and generalized it to fit Go.) For example, it's able to tell in this case that r1 and r2 are the same.

    type fileBox struct {
     slots [2]*os.File
    }

    func example(path string, replacement *os.File) error {
     r1, err := os.Open(path)
     if err != nil {
      return err
     }

     original := fileBox{slots: [2]*os.File{replacement, r1}}
     copied := original

     alias := &original
     alias.slots[1] = replacement

     window := copied.slots[1:]
     r2 := window[0]
     return r2.Close()
    }
It's also capable of detecting certain types of goroutine leaks / lock inversions. For example, the following code gets flagged:

    func runWithTimeout(work func() error) error {
     done := make(chan error)
     go func() { done <- work() }()

     select {
     case err := <-done:
      return err
     case <-time.After(time.Second):
      return errors.New("operation timed out")
     }
    }
HOW THIS WAS BUILT / TRAINED

I'll be up front with y'all and say I used AI a lot to help me in the process of writing gohawk. However, I will stand by the fact that it's not just AI slop -- I'm a clean code fanatic, and the pipeline has been deliberately engineered.

I really wanted the user experience to be buttery smooth when downloading and using this, so I had an AI agent continuously download hundreds of repos from the Internet, run gohawk on them, and report back if there were any false positives. (Yes, I know, slightly overkill :^) ) It's at a point where I think false positives on new code should be fairly rare.

OTHER FEATURES

- Really rich diagnostics (I took inspo from rustc -- the full lineage of the error gets reported, including error spans) - Documentation website (https://gohawk.dev) -- Contains detailed overview of all the supported analyzers. The diagnostic messages link out to the website - Custom analysis runner -- I found `go vet` to be really slow because it would spend a lot of time serializing and deserializing facts across packages, so I rolled my own runner to replace it. - Performant -- From a cold cache, it's able to analyze Docker in around 13s, and the whole of Kubernetes around 76s.

Building this out was a really awesome learning experience for me, and I'm planning to make a blog post about it soon -- stay tuned. Also hoping to get it integrated directly into GolangCI but gotta ask the maintainer first, so fingers crossed.

I genuinely hope you guys can download and enjoy the effort I put into this project. Feedback and contributions are welcome, and I'm planning to set up a Discord or IRC channel in the near future.