rgoussu@goussu: ~/library/go/toolchain
~/library/go/toolchain cat go-vet.md

go vet

# The static analyzer shipped with the toolchain — catches likely-wrong code, runs under go test, extensible via go/analysis.

Conceptsaved 2026-08-09 #go#tooling#static-analysis

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: printf verb/argument mismatches, malformed struct tags, copylocks (copying a sync.Mutex by value), unreachable code, useless comparisons, unmarshal into 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=off disables it, -vet=all widens 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