A go/analysis linter that catches sync.WaitGroup misuse:
Add()called inside the goroutine it registers - should be called beforego func(){...}(), not inside it, orWait()can race ahead of registration.Done()with no matchingAdd()in the same function - this panics at runtime ("negative WaitGroup counter").- A goroutine that calls
Done()without a top-leveldefer 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.
cd waitgroupcheck
go mod tidy # pulls in golang.org/x/tools
go build ./...
go test ./... # runs the analysistest fixture in testdata/src/aIf 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.
go install ./cmd/waitgroupcheck
waitgroupcheck ./...
# or as a go vet tool:
go vet -vettool=$(which waitgroupcheck) ./...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.0That 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.
- Run
go test ./...locally, fix any// wantregex mismatches. - 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.
- Replace the pattern-3 heuristic with a real CFG walk
(
golang.org/x/tools/go/cfg, same packagebodycloseuses) once you have real false-positive data to know if it's worth the complexity. - Add the
pluginpackage and try it via golangci-lint's module plugin system on a real repo before proposing anything upstream.