gofmt и go vet

Материал из llmdocs — справочник разработчика
Портал: Go и Rust
gofmt и go vet
ЯзыкGo
gofmtформатирование, без настроек
go vetстатический анализ подозрительного кода
Поставляютсявместе с Go (go fmt, go vet)
Сторонниеgoimports, staticcheck, golangci-lint, gofumpt
Интеграцияgopls (сервер языка), pre-commit, CI

gofmt и go vet — два инструмента, которые поставляются вместе с Go. gofmt автоматически приводит исходный код к единственному каноническому виду, а go vet ищет конструкции, которые компилируются, но почти наверняка являются ошибками. Они задали культуру Go-сообщества: споры о стиле закончились, потому что стиль один, а базовый статический анализ запускается одной командой без настройки.[1][2]

gofmt

gofmt появился одновременно с языком в 2009 году. Его ключевая особенность — отсутствие настроек: нет выбора между табами и пробелами, нет длины строки, нет вариантов расстановки скобок. Отступы — табы, выравнивание — пробелы, открывающая фигурная скобка — на той же строке (это требует и сам синтаксис из-за автоматической вставки точек с запятой).[4] Роб Пайк сформулировал это так: «стиль gofmt никому не нравится, но gofmt нравится всем».[3]

gofmt -l .        # список файлов, которые отформатированы не так
gofmt -d main.go  # показать diff
gofmt -w .        # переписать файлы на месте
go fmt ./...      # то же для всех пакетов модуля (gofmt -l -w)
gofmt -s -w .     # дополнительно упростить код

Флаг -s упрощает выражения: например, []T{T{1}, T{2}} превращается в []T{{1}, {2}}, а s[a:len(s)] — в s[a:]. Флаг -r позволяет делать механические замены по шаблону: gofmt -r 'bytes.Compare(a, b) == 0 -> bytes.Equal(a, b)' -w ..

Поскольку формат детерминирован, gofmt безопасно запускать автоматически: при сохранении в редакторе (через сервер языка gopls), в pre-commit и в CI/CD. Диффы в ревью содержат только смысловые изменения. Идея оказалась заразительной: rustfmt в Rust (см. Clippy и rustfmt), Black и Ruff в Python, Prettier в JavaScript переняли подход «форматтер без споров».

goimports и gofumpt

  • goimports делает всё то же, что gofmt, и дополнительно добавляет недостающие и удаляет неиспользуемые импорты, группируя стандартную библиотеку отдельно. Многие редакторы используют именно его.
  • gofumpt — более строгий форматтер-надмножество: убирает лишние пустые строки, объединяет объявления и т. п. Его результат по-прежнему совместим с gofmt.

go vet

go vet — статический анализатор, который проверяет корректность, а не стиль. Он запускается как go vet ./... и, что важно, автоматически выполняет часть проверок при go test.[2] Набор анализаторов включает:

Анализатор Что ловит
printf несоответствие формата и аргументов в fmt.Printf("%d", "строка")
copylocks копирование sync.Mutex по значению
loopclosure захват переменной цикла в горутине (актуально до Go 1.22)
lostcancel context.WithCancel без вызова cancel
structtag некорректные теги полей вроде json:name без кавычек
unreachable недостижимый код
httpresponse использование resp.Body до проверки ошибки
nilfunc, bools, assign бессмысленные сравнения и присваивания
unusedresult игнорирование результата чистых функций вроде fmt.Sprintf

Пример, который скомпилируется, но vet отклонит:

var mu sync.Mutex
func f(m sync.Mutex) {}   // vet: f passes lock by value
fmt.Printf("%s\n", 42)    // vet: Printf format %s has arg 42 of wrong type int

Анализаторы построены на общем фреймворке golang.org/x/tools/go/analysis, поэтому их легко комбинировать и писать собственные.[7] Отдельно стоит детектор гонок -race — он не статический, а работает во время выполнения тестов (см. Состояние гонки и блокировки).

Более строгие линтеры

go vet намеренно консервативен: в нём только проверки почти без ложных срабатываний. Для большего используют:

  • staticcheck — самый известный сторонний анализатор: ошибки (SA), упрощения (S), стиль (ST), устаревшие API; многие его проверки в итоге попадают в gopls.[5]
  • golangci-lint — агрегатор, который запускает десятки линтеров (vet, staticcheck, errcheck, gosec, revive, ineffassign и др.) параллельно, с общим кэшем и одним конфигом .golangci.yml.[6] Это стандарт де-факто для CI.
# .golangci.yml (фрагмент)
version: "2"
linters:
  enable: [errcheck, govet, staticcheck, gosec, revive]
formatters:
  enable: [gofumpt, goimports]

Линтер errcheck особенно полезен: он находит проигнорированные ошибки, что для Go с его явной обработкой ошибок — частый источник багов.

В CI

Типичная проверка в пайплайне GitHub Actions:

test -z "$(gofmt -l .)"   # упасть, если что-то не отформатировано
go vet ./...
golangci-lint run
go test -race ./...

Набор из форматтера, vet, линтера и тестов с -race дёшев и покрывает большую часть типовых ошибок ещё до ревью.

См. также

Источники

  1. Go — Command gofmt
  2. Go — Command vet
  3. Go Blog — go fmt your code
  4. Effective Go — Formatting
  5. Staticcheck documentation
  6. golangci-lint documentation
  7. Go — golang.org/x/tools/go/analysis