Разработка & Кодинг Jul 26, 2026В закладки

Под капотом `go vet`, `staticcheck` и подобных инструментов: команда Go поддерживает официальный фреймворк, который позволяет написать линтер всего в несколько десятков строк.
Вы наследуете проект на Go объёмом 200 000 строк. Ревью кода на протяжении шести месяцев пропускает паттерн, вызывающий утечки горутин: context.WithTimeout создаётся без defer cancel(). Никто не замечает этого при чтении. go vet не сигнализирует об ошибке. Добавление правила в домашний линтер в другом языке заняло бы около недели.
В Go вы пишете Analyzer за один вечер. Потому что команда языка Go давно поддерживает почти незаметный, но при этом фундаментальный пакет: golang.org/x/tools/go/analysis.
Пакет go/analysis (« Go Analysis Framework ») не нов — он опубликован и задокументирован на pkg.go.dev/golang.org/x/tools/go/analysis — но регулярно всплывает на Hacker News (последнее упоминание 26 июля 2026 года), потому что многие разработчики на Go всё ещё о нём не знают. А это инфраструктура, на которой строится весь экосистем анализа статического кода языка: go vet использует его, staticcheck (Dominik Honnef) основан на нём, gopls (официальный language server) подключает к нему свои диагностики, и подавляющее большинство линтеров в современных CI — во главе с golangci-lint — это просто наборы Analyzer, сложенные вместе.
Пари этого фреймворка прост: один анализатор = одна структура. Эта структура объявляет своё имя, документацию, зависимости от других анализаторов и функцию Run, которая принимает объект *analysis.Pass (парсинг кода, проверка типов, файлы, комментарии) и сообщает о диагностиках через pass.Report. Вот и всё.
Три идеи лежат в основе архитектуры:
Модульность через композицию. Анализатор может объявить Requires: []*analysis.Analyzer{inspect.Analyzer}: фреймворк решает граф зависимостей и внедряет результат предварительных анализаторов в Pass.ResultOf. Результат: ваш бизнес-анализатор не пере-парсит файлы, он потребляет результат inspect (который, в свою очередь, обходит типизированный AST всего один раз для всей цепочки).
Один «драйвер», несколько вариантов загрузки. Вы пишете свой Analyzer. singlechecker.Main(myAnalyzer) превращает его в автономный CLI-бинарь, multichecker.Main(a1, a2, a3) — в мульти-чекер, unitchecker.Main(...) подключает его к go vet -vettool=.... Никакого glue-кода: тот же код Analyzer работает локально, в CI, в IDE через gopls и в golangci-lint.
Автоматическое исправление. Диагностика может содержать SuggestedFix: текстовые замены, привязанные к позициям token.Pos. gopls применяет их в один клик в редакторе. Благодаря этому staticcheck может предлагать «заменить errors.Is(err, sql.ErrNoRows) на более идиоматическую проверку», а IDE — выполнять подстановку корректно.
Для масштаба: singlechecker — функция, превращающая *Analyzer в полноценный бинарь, — помещается в один файл. Минимальный шаблон публикуемого линтера на Go — это примерно 40 строк, включая документацию и тесты. Сравните с написанием плагина ESLint с нуля: это не тот же масштаб.
Три сценария, которые встречаются в реальных проектах:
net/http напрямую, нужно использовать наш обёртку для трейсинга.» Тридцать строк, анализатор, который инспектирует *ast.ImportSpec, тест с analysistest.Run на фикстурном файле — и правило подключено в CI уже на следующий день.SuggestedFix превращает операцию в go vet -vettool=./monfix -fix ./.... Человек в процессе не участвует.ctrlflow (граф потока управления), buildssa (представление SSA кода Go) — на основе которых можно строить нетривиальные анализы потока данных. Например, обнаружение не закрытых sql.Rows становится серьёзным делом с использованием SSA, а не сырого AST.Две слепые зоны, о которых стоит помнить:
analysis.Fact), которые позволяют анализатору экспортировать факты о символах пакета, чтобы их могли использовать другие пакеты, но это заметно сложнее в реализации.Analyzer к go vet через рантайм-конфигурацию: его нужно скомпилировать в бинарь. Это не упущение, а осознанный выбор (Go не любит динамические плагины).go/analysis — это официальное API для всего, что связано с анализом статического кода на Go, от домашнего линтера до автоматической миграции кода.Run(*Pass). Всё остальное (CLI, интеграция с go vet, интеграция с gopls, CI) вам предоставляется.sed, эта одна ночь точно не будет потрачена зря.Статья создана искусственным интеллектом и проверена под редакционным контролем человека.