gox

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: BSD-3-Clause

README

gox

Strict static analyzer for Go. Zero external dependencies — every rule is implemented from scratch on top of go/ast, go/types, and the go list command.

The goal: catch the classes of bugs an LLM writing Go without supervision is most likely to introduce. Be loud, be opinionated, fail closed.

Status: experimental. Built as a tool for using Claude Code (and similar agents) to write Go without leaving silent bugs behind. Used daily, but the rule set is still evolving — expect breaking changes to annotation syntax until v1.0.

Install

go install github.com/mentasystems/gox/cmd/gox@latest

Use

gox check ./...    # analyze; exit 1 on any issue
gox list           # list registered analyzers
gox build [args]   # gox check && go build
gox test  [args]   # gox check && go test

Rules

Analyzer What it catches
errcheck error return values dropped silently
shadow := re-declaring an outer variable (except ok)
forcetypeassert x := v.(T) without the comma-ok form
namedargs call sites passing 2+ args of the same basic type without /* paramName */ comments (user code only — stdlib calls exempt)
exhaustive non-exhaustive switch on iota enums or sealed interfaces
noglobals mutable package-level var declarations
banany any / interface{} in declarations without justification
bodyclose *http.Response.Body left unclosed
contextcheck context.Background()/TODO() inside a function that already receives a context.Context
goroutine go f() without a visible *errgroup.Group, sync.WaitGroup, or context.CancelFunc

Annotations

Every rule has a single opt-out marker. Annotations must include a reason after the colon — empty reasons are ignored.

Comment Effect
// safe-ignore: <why> Suppress errcheck, forcetypeassert, bodyclose, contextcheck on the same line
// global-ok: <why> Allow a package-level var (noglobals)
// any-ok: <why> Allow any / interface{} (banany)
// goroutine-ok: <why> Allow a fire-and-forget go statement (goroutine)
// exhaustive-ok: <why> Accept a default: case as covering missing variants (exhaustive)

Claude Code integration

gox install claude

Writes ~/.claude/gox-hook.sh and registers it as a PostToolUse hook in ~/.claude/settings.json (matcher Edit|Write|MultiEdit, timeout 30s). Idempotent — running it again just refreshes the script. Preserves every other key in settings.json.

Whenever Claude edits a .go file, the hook runs gox check . in the file's directory. If issues are found, the hook returns a decision:block JSON blob with the full output, which Claude sees on its next turn and must fix or annotate before continuing.

Claude Code only re-reads settings.json when the /hooks menu is opened or the app is restarted, so the hook activates in a new session (or after opening /hooks once in the current one).

The hook resolves gox via $GOX_BIN if set, otherwise ~/go/bin/gox. Run go install github.com/mentasystems/gox/cmd/gox@latest to make sure it's present.

Performance

A pure-Go implementation with no runtime overhead from external linters. On a 442-package monorepo (~1800 .go files):

Time
Cold run (no cache) ~9.4s
Warm run (full cache hit) ~2.6s

The cache is per-package, keyed by file mtime+size and the analyzer set hash. It lives under $XDG_CACHE_HOME/gox/v2 (or ~/.cache/gox/v2). Pass --no-cache to disable.

Generated code

Files marked with the standard Go marker // Code generated <generator> DO NOT EDIT. near the top are skipped automatically. This covers protoc-gen-go, yo, mockgen, and most common generators.

Design notes

  • Zero external dependencies. Everything uses Go stdlib + a shell-out to go list -json. No golang.org/x/tools, no third-party linter packages. Mantenimiento mínimo: cuando salga una nueva versión de Go, no hay nada que actualizar.
  • Fail closed. Every rule defaults to error. Opt-outs require an explicit annotation with a written reason — the reason is the documentation.
  • Targeted at LLM-written code. Heuristics are tuned so each rule catches a high-frequency LLM bug class without flooding human-readable idioms. For example, shadow exempts ok (the universal comma-ok name) but still catches err re-declaration, which is exactly the bug we want.
  • namedargs is the killer rule. Two adjacent string/int/bool parameters in user-defined code force the call site to label them with inline comments. Stdlib calls are exempt because their conventions are memorized. The bug it prevents — transfer(orderID, userID) vs transfer(userID, orderID) — produces no compile error and no test failure, and is the single most common silent-bug class in unsupervised AI-written Go.

Contributing

Issues and pull requests welcome. Two ground rules:

  1. No external dependencies. Every rule must be implementable with the Go standard library plus an out-of-process go list -json call. Adding golang.org/x/tools/go/packages, staticcheck, or any third-party linter is out of scope.
  2. Each new rule must pass its own check. Run gox check ./... on a fresh clone before opening a PR — gox runs against its own source as part of the smoke test.

License

BSD 3-Clause. See LICENSE.

Directories

Path Synopsis
cmd
gox command
gox is a strict static analyzer for Go.
gox is a strict static analyzer for Go.
internal
astutil
Package astutil contains small helpers shared by multiple analyzers.
Package astutil contains small helpers shared by multiple analyzers.
pkg
analyzer
Package analyzer defines the core types every gox analyzer implements.
Package analyzer defines the core types every gox analyzer implements.
analyzers/banany
Package banany forbids `any` / `interface{}` in declarations without an `// any-ok: <reason>` annotation on the same line (or on a doc comment).
Package banany forbids `any` / `interface{}` in declarations without an `// any-ok: <reason>` annotation on the same line (or on a doc comment).
analyzers/bodyclose
Package bodyclose reports HTTP responses whose Body is never closed.
Package bodyclose reports HTTP responses whose Body is never closed.
analyzers/contextcheck
Package contextcheck enforces context propagation.
Package contextcheck enforces context propagation.
analyzers/errcheck
Package errcheck reports calls to functions returning `error` whose error value is silently dropped.
Package errcheck reports calls to functions returning `error` whose error value is silently dropped.
analyzers/exhaustive
Package exhaustive enforces switch exhaustiveness over enums and sealed interfaces defined in the analyzed packages.
Package exhaustive enforces switch exhaustiveness over enums and sealed interfaces defined in the analyzed packages.
analyzers/forcetypeassert
Package forcetypeassert forbids type assertions that would panic on failure.
Package forcetypeassert forbids type assertions that would panic on failure.
analyzers/goroutine
Package goroutine reports `go` statements whose lifetime is not bound to a visible coordination primitive.
Package goroutine reports `go` statements whose lifetime is not bound to a visible coordination primitive.
analyzers/namedargs
Package namedargs requires call sites to name their arguments when two or more consecutive parameters share the same type.
Package namedargs requires call sites to name their arguments when two or more consecutive parameters share the same type.
analyzers/noglobals
Package noglobals forbids package-level mutable `var` declarations.
Package noglobals forbids package-level mutable `var` declarations.
analyzers/shadow
Package shadow reports variable shadowing introduced by `:=`.
Package shadow reports variable shadowing introduced by `:=`.
baseline
Package baseline implements the "ignore pre-existing issues" workflow.
Package baseline implements the "ignore pre-existing issues" workflow.
cache
Package cache provides a per-package incremental cache for gox analyzer results.
Package cache provides a per-package incremental cache for gox analyzer results.
loader
Package loader parses and type-checks Go packages using only the standard library.
Package loader parses and type-checks Go packages using only the standard library.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL