go/analysis : фреймворк модульного статического анализа от команды Go, состоящий из небольших повторно используемых компонентов

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

go/analysis : фреймворк модульного статического анализа от команды Go, состоящий из небольших повторно используемых компонентов
Иллюстрация : Momiji Shirogane

Под капотом `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. Вот и всё.

Как это работает под капотом

Три идеи лежат в основе архитектуры:

  1. Модульность через композицию. Анализатор может объявить Requires: []*analysis.Analyzer{inspect.Analyzer}: фреймворк решает граф зависимостей и внедряет результат предварительных анализаторов в Pass.ResultOf. Результат: ваш бизнес-анализатор не пере-парсит файлы, он потребляет результат inspect (который, в свою очередь, обходит типизированный AST всего один раз для всей цепочки).

  2. Один «драйвер», несколько вариантов загрузки. Вы пишете свой Analyzer. singlechecker.Main(myAnalyzer) превращает его в автономный CLI-бинарь, multichecker.Main(a1, a2, a3) — в мульти-чекер, unitchecker.Main(...) подключает его к go vet -vettool=.... Никакого glue-кода: тот же код Analyzer работает локально, в CI, в IDE через gopls и в golangci-lint.

  3. Автоматическое исправление. Диагностика может содержать SuggestedFix: текстовые замены, привязанные к позициям token.Pos. gopls применяет их в один клик в редакторе. Благодаря этому staticcheck может предлагать «заменить errors.Is(err, sql.ErrNoRows) на более идиоматическую проверку», а IDE — выполнять подстановку корректно.

Для масштаба: singlechecker — функция, превращающая *Analyzer в полноценный бинарь, — помещается в один файл. Минимальный шаблон публикуемого линтера на Go — это примерно 40 строк, включая документацию и тесты. Сравните с написанием плагина ESLint с нуля: это не тот же масштаб.

Что это меняет на практике

Три сценария, которые встречаются в реальных проектах:

  • Внутренние правила компании. «Запрещено импортировать net/http напрямую, нужно использовать наш обёртку для трейсинга.» Тридцать строк, анализатор, который инспектирует *ast.ImportSpec, тест с analysistest.Run на фикстурном файле — и правило подключено в CI уже на следующий день.
  • Массовая миграция кода. Рефакторинг в большом масштабе (переименование устаревшего API в 500 файлах): анализатор с SuggestedFix превращает операцию в go vet -vettool=./monfix -fix ./.... Человек в процессе не участвует.
  • Кастомный анализ потока данных. Фреймворк предоставляет сопутствующие пакеты — ctrlflow (граф потока управления), buildssa (представление SSA кода Go) — на основе которых можно строить нетривиальные анализы потока данных. Например, обнаружение не закрытых sql.Rows становится серьёзным делом с использованием SSA, а не сырого AST.

Ограничения, которые нужно знать

Две слепые зоны, о которых стоит помнить:

  • Анализ внутри пакета. По умолчанию анализатор видит один пакет за раз. Межпакетный анализ (whole-program) требует дополнительной работы — это возможно с помощью «фактов» (analysis.Fact), которые позволяют анализатору экспортировать факты о символах пакета, чтобы их могли использовать другие пакеты, но это заметно сложнее в реализации.
  • Нет экосистемы динамических плагинов. Нельзя «подключить» Analyzer к go vet через рантайм-конфигурацию: его нужно скомпилировать в бинарь. Это не упущение, а осознанный выбор (Go не любит динамические плагины).

Что нужно запомнить

  • go/analysis — это официальное API для всего, что связано с анализом статического кода на Go, от домашнего линтера до автоматической миграции кода.
  • Один анализатор = одна структура с методом Run(*Pass). Всё остальное (CLI, интеграция с go vet, интеграция с gopls, CI) вам предоставляется.
  • Если вы до сих пор пишете правила контроля качества кода на shell или sed, эта одна ночь точно не будет потрачена зря.
Resources

Статья создана искусственным интеллектом и проверена под редакционным контролем человека.

Наша редакция
Была ли статья полезной?

3 чел. оценили эту статью

Нравится
K
Kaito KuroganeSenior Dev Writer
Senior polyvalent developer, backend Go + frontend TS, open source contributor.
Поделиться:
LIVERadio Geek Kitsune
Нажми и слушай — один звук для всех
0··
// Расписание
// all stations
// поделиться треком →
Темы
Обзор
Информация