Overview
go vet examines source for constructs that compile but are probably wrong. It ships with
the toolchain, needs no configuration, and its high-confidence subset runs automatically
before go test — so every Go developer runs a static analyzer daily whether they know it
or not.
Key points
- What it catches:
printfverb/argument mismatches, malformed struct tags,copylocks(copying async.Mutexby value), unreachable code, useless comparisons,unmarshalinto non-pointers, atomic misuse, context/cancel leaks — around thirty analyzers, all tuned for near-zero false positives. - loopclosure: historically the headline check (goroutines capturing the loop
variable); largely defused by the Go 1.22 per-iteration loop-variable semantics, but the
analyzer remains for older-
go-directive modules. - Runs under
go test: a curated high-signal subset (printf, struct tags, buildtags…) vets packages as part of every test run;go test -vet=offdisables it,-vet=allwidens it. - Selective invocation:
go vet -printf=false ./...toggles analyzers; each analyzer is independently addressable. - go/analysis framework: each check is an
analysis.Analyzer— the same API powers staticcheck, gopls diagnostics, and custom in-house checkers;go vet -vettool=$(which yourchecker)swaps in your own driver. Writing a company-specific analyzer is a well-trodden path. - Relation to staticcheck: vet stays deliberately small and certain; broader or more opinionated analysis (unused code, simplifications, API misuse) belongs to staticcheck & golangci-lint — same framework, larger surface.
Examples
go vet ./...
go vet -structtag=false ./legacy/... # silence one analyzer
go test ./... # vet's subset runs implicitly first
Related
- The Go toolchain — parent catalog.
- staticcheck & golangci-lint — the community extension of the same idea.
- go test — the command vet piggybacks on.
- gopls — surfaces analysis diagnostics live in the editor.