Skip to content

Repository files navigation

waitgroupcheck

A go/analysis linter that catches sync.WaitGroup misuse:

  1. Add() called inside the goroutine it registers - should be called before go func(){...}(), not inside it, or Wait() can race ahead of registration.
  2. Done() with no matching Add() in the same function - this panics at runtime ("negative WaitGroup counter").
  3. A goroutine that calls Done() without a top-level defer wg.Done()
    • early-return or panic paths can skip a non-deferred call.

See the doc comment at the top of waitgroupcheck.go for the exact scope and known limitations of the v0.1 heuristics.

Setup (this sandbox has no Go toolchain, so do this locally)

cd waitgroupcheck
go mod tidy        # pulls in golang.org/x/tools
go build ./...
go test ./...       # runs the analysistest fixture in testdata/src/a

If go test fails on the fixture, it's almost certainly a mismatch between the // want regex comments in testdata/src/a/a.go and the actual pass.Reportf message text as that's the first thing to check since I couldn't execute this to verify it end-to-end.

Running it standalone

go install ./cmd/waitgroupcheck
waitgroupcheck ./...
# or as a go vet tool:
go vet -vettool=$(which waitgroupcheck) ./...

Wiring into golangci-lint as a module plugin

golangci-lint's module plugin system expects a NewAnalyzer (or New) entry point of a specific shape and a .custom-gcl.yml build config. Rough shape once this is stable enough to try:

# .custom-gcl.yml in the consuming repo
version: v1.62.0
plugins:
  - module: 'github.com/BlackBuck/waitgroupcheck'
    import: 'github.com/BlackBuck/waitgroupcheck/plugin'
    version: v0.1.0

That needs a small plugin package wrapping Analyzer in golangci-lint's plugin interface (register.LinterPlugin). It's not included here yet since it's only worth writing once the core checks are validated against real code and the false-positive rate is actually known, not just designed-for.

Next steps

  1. Run go test ./... locally, fix any // want regex mismatches.
  2. Run it against a real codebase (your Pcom-Go / govalid repos, or anything with real goroutine fan-out) and see what it flags — the defer-presence heuristic for pattern 1 is the most likely source of false positives, since it doesn't prove every path defers, just that a top-level defer exists.
  3. Replace the pattern-3 heuristic with a real CFG walk (golang.org/x/tools/go/cfg, same package bodyclose uses) once you have real false-positive data to know if it's worth the complexity.
  4. Add the plugin package and try it via golangci-lint's module plugin system on a real repo before proposing anything upstream.

About

Check imbalanced wait groups in go

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages