Go tooling and static analysis
Skill ctoth/golang-skills-plugin/plugins/golang/skills/go-tooling-and-static-analysis
Guides the Go detection layer — the CI gate that catches the rules the authoring skills teach. gofmt/gofumpt for canonical formatting; go vet for built-in correctness analyzers (printf, copylocks, lostcancel, loopclosure, stringintconv, waitgroup); staticcheck for deeper bug/simplification/style checks; golangci-lint as the curated aggregator (do NOT enable-all); govulncheck for reachable known vulnerabilities; go test -race; //go:build constraints; go generate; and the Go 1.26 go fix modernizers. Auto-invokes when setting up CI/linting, configuring golangci-lint/.golangci.yml, running go vet/staticcheck/govulncheck/gofmt, build tags, go generate, or on "add linting" / "why is CI failing on format" / "how do I catch this class of bug". A discarded error or a copied lock is found by the toolchain, not by reading one file.From its SKILL.md
npx -y skills add ctoth/golang-skills-plugin --skill go-tooling-and-static-analysisAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
14.9 KB, ~3.7k tokens by cl100k_base, as published. Nobody here has run it
Go Tooling and Static Analysis
"Gofmt's style is no one's favorite, yet gofmt is everyone's favorite." — Go Proverbs
"Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string ... it can find errors not caught by the compilers." — cmd/vet
The other Go skills teach the rules. This skill is the gate that enforces them mechanically. A discarded error, a copied lock, a missing cancel, a string(int) conversion, a leaked goroutine — each is a rule an authoring skill states and a finding a tool emits. Reading one file finds none of them reliably; a CI pipeline of gofmt + go vet + a deeper linter + govulncheck + -race finds them every time, on every commit. This skill is the detection hub: it names the tools, ties each high-value check back to the authoring skill it enforces, and gives a curated CI pipeline (references/ci-pipeline.md).
1. gofmt / gofumpt — Canonical Formatting Is Not a Debate
"Gofmt is a tool that automatically formats Go source code" (gofmt blog). It is non-negotiable: format is decided by the tool, never by hand, never in review. The payoff is four-fold — code is easier to write ("never worry about minor formatting concerns"), read ("when all code looks the same you need not mentally convert others' formatting style"), and maintain ("diffs show only the real changes"), and it is "uncontroversial" (gofmt blog).
In CI you do not apply gofmt, you check it. gofmt -l lists files that are not formatted; a non-empty list fails the build:
# CI gate: prints any unformatted file; fails if the list is non-empty
test -z "$(gofmt -l .)" # or: gofmt -l . | tee /dev/stderr | (! read)
# Locally, fix in place:
gofmt -w .
gofumpt is a stricter superset: "Enforce a stricter format than gofmt, while being backwards compatible. That is, gofumpt is happy with a subset of the formats that gofmt is happy with"; "running gofmt after gofumpt should produce no changes" (gofumpt). Adopt it as a drop-in (gofumpt -l -w .) when the team wants the extra rules; it is also a golangci-lint formatter. Formatting is owned at the policy level by go-naming-and-style ("gofmt decides"); this skill owns wiring the check into CI.
2. go vet — Always in CI
go vet ships with the toolchain and "uses heuristics that do not guarantee all reports are genuine problems, but it can find errors not caught by the compilers" (cmd/vet). It is the floor of static analysis — run go vet ./... in every CI pipeline. go test already runs a subset of vet, but the explicit step covers all default analyzers.
Each high-value analyzer detects a rule an authoring skill teaches:
go vet analyzer | What it catches (verbatim) | Enforces the rule in |
|---|---|---|
printf | "check consistency of Printf format strings and arguments" | go-slog-logging, format-string correctness |
copylocks | "check for locks erroneously passed by value" | go-sync-primitives (never copy a Mutex) |
lostcancel | "check cancel func returned by context.WithCancel is called" | go-context (always defer cancel()) |
loopclosure | "check references to loop variables from within nested functions" | go-concurrency-goroutines (loop-var capture) |
stringintconv | "check for string(int) conversions" | go-strings-bytes-runes (the string(n) rune trap) |
waitgroup (1.25) | "check for misuses of sync.WaitGroup" | go-concurrency-goroutines (misplaced Add) |
hostport (1.25) | "check format of addresses passed to net.Dial" | host:port construction bugs |
composites | "check for unkeyed composite literals" | go-zero-values-and-construction (keyed literals) |
errorsas | "report passing non-pointer or non-error values to errors.As" | go-error-handling (errors.As target) |
Note: the shadow analyzer (variable shadowing) is not in the default set — it is off by default and shipped separately under golang.org/x/tools/go/analysis/passes/shadow; enable it deliberately if you want it, but expect more noise.
3. staticcheck — The De-Facto Deeper Analyzer
Where go vet is the floor, staticcheck is "a state of the art linter for the Go programming language ... it finds bugs and performance issues, offers simplifications, and enforces style rules" (staticcheck). It "focuses on checks that produce few to no false positives," so its warnings are signal, not noise. "Just run staticcheck ./... on your code in addition to go vet ./..." (staticcheck).
Its checks are grouped by prefix (staticcheck checks):
SA(codenamedstaticcheck) — "all checks that are concerned with the correctness of code." Real bugs: misused stdlib, impossible conditions, ineffective assignments.S(codenamedsimple) — "all checks that are concerned with simplifying code."ST(codenamedstylecheck) — "all checks that are concerned with stylistic issues."QF(codenamedquickfix) — refactorings "used as part of gopls."
Keep at least the SA family on; S/ST are valuable but more opinionated. Pin the staticcheck version in CI: a staticcheck older than your Go release can fail to parse newer export data (a real failure mode on bleeding-edge toolchains).
4. golangci-lint — Aggregator: Curate, Do Not enable-all
golangci-lint is "a fast linters runner for Go" that "runs linters in parallel, uses caching, supports YAML configuration ... and includes over a hundred linters" (golangci-lint). It bundles govet, staticcheck, ineffassign, errcheck, and many more behind one fast, cached golangci-lint run.
The key discipline: do not enable every linter. The config's linters.default can be all, but all is the wrong default — new releases keep adding linters, so an all config silently turns on new, sometimes conflicting checks and breaks CI on every upgrade, and floods you with low-value or contradictory findings. Start from the curated standard set (the documented default) and add linters deliberately:
# .golangci.yml — curate up from the default, never down from `all`
version: "2"
linters:
default: standard # NOT `all`; add specific linters below
enable:
- errcheck # discarded errors -> go-error-handling
- errorlint # %v-instead-of-%w, == on wrapped errs -> go-error-handling
- govet # the vet analyzers (§2)
- staticcheck # SA/S/ST (§3)
- ineffassign # assignments never read
- bodyclose # unclosed HTTP response bodies
A fuller starting .golangci.yml, with each linter tied to the skill it enforces, is in references/ci-pipeline.md. Run it with golangci-lint run ./....
5. govulncheck — Known Vulnerabilities You Actually Call
govulncheck scans dependencies against the Go vulnerability database (https://vuln.go.dev) but "reduces noise by prioritizing vulnerabilities in functions that your code is actually calling" (govulncheck blog). "It uses static analysis of source code or a binary's symbol table to narrow down reports to only those that could affect the application" (pkg.go.dev). That reachability analysis is what makes it low-noise: a CVE in a module you import but never call into is reported separately and does not fail you.
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
Real output distinguishes the two cases sharply: a vuln your code calls comes with the exact trace (main.main calls language.Parse), while vulns merely present in imported packages or required modules are summarized as not affecting you. Wire govulncheck ./... into CI; it exits non-zero only when your code calls a vulnerable symbol. The go.mod/go.sum upgrade path that fixes a finding is owned by go-modules-and-versioning.
6. go test -race — The Detector for the Bug You Can't Read
A data race is invisible to formatters and analyzers — it is a runtime property. The race detector instruments memory access and reports concurrent unsynchronized access at runtime. Run go test -race ./... in CI. "It passed once" is not proof; only the detector exercising the racy path is. The memory model, what a race is, and testing/synctest are owned by go-race-and-memory-model; this skill owns the CI-wiring fact: -race belongs in the test step of every pipeline.
7. go generate — Codegen Is Explicit and Committed
"Generate runs commands described by directives within existing files. Those commands can run any process but the intent is to create or update Go source files" (cmd/go). Critically: "Go generate is never run automatically by go build, go test, and so on. It must be run explicitly." The directive is a comment with no leading space:
//go:generate stringer -type=Color
You run go generate ./..., the tool writes the _string.go (or mock) file, and you commit the generated output. Two CI consequences: (1) generated code is in the repo, reviewed like any other; (2) a CI step can run go generate ./... and then fail if git diff is non-empty — proving the committed generated code is not stale. stringer for enums is owned by go-zero-values-and-construction.
8. Build Constraints — //go:build, the Modern Form
"A build constraint, also known as a build tag, is a condition under which a file should be included in the package," given by a line beginning //go:build (cmd/go). It must appear near the top of the file, before the package clause, followed by a blank line. The satisfied tags include the target OS (runtime.GOOS, set via GOOS) and architecture (GOARCH).
//go:build linux
package platform
// This file compiles only when GOOS=linux; excluded on other targets.
Use the modern //go:build form, not the legacy // +build form (the old syntax is error-prone and superseded; vet's buildtag analyzer checks both). "It is an error for a file to have more than one //go:build line." Verified behavior: a default go build on a non-Linux host excludes a //go:build linux file (referencing a symbol it defines fails with undefined), while GOOS=linux go build includes it and succeeds.
9. go fix Modernizers — Auto-Rewrite Old Idioms (Go 1.26)
As of Go 1.26, "the venerable go fix command has been completely revamped and is now the home of Go's modernizers. It provides a dependable, push-button way to update Go code bases to the latest idioms and core library APIs" (Go 1.26). It "builds atop the exact same Go analysis framework as go vet," so the same analyzers that diagnose in vet can rewrite in fix. The suite rewrites old patterns to modern ones (loop-var idioms, min/max/clear, stdlib helpers) and includes "a source-level inliner that allows users to automate their own API migrations using //go:fix inline directives."
//go:fix inline
func OldName(x int) int { return NewName(x) } // callers get rewritten to NewName
go fix is the tool; the human-readable table of which idiom each Go version unlocks (so "modernize" means modernize to the version your go directive allows) is owned by go-version-feature-map.
10. The Recommended CI Pipeline
The detection layer is a sequence, fast checks first:
gofmt -l .(orgofumpt -l .) — fail if any file is unformatted.go vet ./...— built-in correctness analyzers (§2).staticcheck ./...orgolangci-lint run ./...— deeper bug/simplification/style checks (golangci-lint bundles vet + staticcheck, so it can replace steps 2–3).govulncheck ./...— reachable known vulnerabilities.go test -race ./...— tests plus the race detector.- (optional)
go generate ./...+git diff --exit-code— prove generated code is fresh.
The concrete, copy-paste pipeline and a curated .golangci.yml, each line annotated with what it catches and which authoring skill it enforces, are in references/ci-pipeline.md.
11. Routing to Related Skills
This skill is the detection hub; every authoring skill links to it for "how is this caught in CI," and it links back to each for the rule:
go-idiomatic-discipline— the policy root; this skill is the gate that detects its violations.go-error-handling—errcheck(discarded errors),errorlint(%v-instead-of-%w,==on wrapped errors), veterrorsas.go-sync-primitives— vetcopylocks(never copy a lock).go-context— vetlostcancel(missingdefer cancel()).go-strings-bytes-runes— vetstringintconv(thestring(int)rune trap).go-concurrency-goroutines— vetloopclosureandwaitgroup(1.25).go-race-and-memory-model—go test -racein CI (§6).go-modules-and-versioning—govulncheck,go mod tidy, the upgrade path that fixes a finding.go-version-feature-map—go fixmodernizers; the table of what each version's idiom modernizes to.go-naming-and-style—gofmtas the formatting authority (this skill wires the check into CI).
12. Reference Files
High-frequency tooling/CI anti-patterns in LLM-generated Go, each with wrong/right commands or config and citations:
${CLAUDE_SKILL_DIR}/references/common-mistakes.md
The recommended CI pipeline (commands in order) and a curated starting .golangci.yml, each line tied to what it catches:
${CLAUDE_SKILL_DIR}/references/ci-pipeline.md
Source provenance for every claim in this skill:
${CLAUDE_SKILL_DIR}/references/sources.yaml
What ships with it: 3 files
20.1 KB alongside SKILL.md
references/
- ci-pipeline.md5.9 KB
- common-mistakes.md8.7 KB
- sources.yaml5.6 KB