Go Analysis Framework описывает модульный статический анализ Go-кода

Пакет golang.org/x/tools/go/analysis задаёт общий интерфейс между статическим анализатором и программой-драйвером, которая запускает анализ. Статический анализатор проверяет пакет Go-кода, выдаёт диагностические сообщения об ошибках и может предлагать рефакторинг или вычислять дополнительные результаты. Анализатор, сообщающий об ошибках, в документации называют проверщиком; в качестве примера приведён проверщик printf, находящий ошибки в строках формата fmt.Printf.

Модульный анализ работает с одним пакетом за раз, но может сохранять сведения о нижележащих пакетах и использовать их при проверке более высокоуровневых, по аналогии с раздельной компиляцией. Например, printf-анализатор фиксирует, что log.Fatalf делегирует вызов fmt.Printf, и затем проверяет обращения к такой функции, в том числе из другого пакета. Общий интерфейс позволяет выбирать и повторно использовать анализаторы из разных источников в командных инструментах вроде vet, редакторах и IDE, системах сборки и тестирования, включая go build, Bazel и Buck, средствах код-ревью, индексаторах кода, просмотрщиках документации и пакетных процессах для больших кодовых баз.

Основной тип API, Analyzer. В нём описываются имя и документация анализатора, его параметры, зависимости от других анализаторов, тип результата и функция Run. Драйвер импортирует нужные Analyzer и запускает их; зависимости в Requires задают порядок выполнения, а Validate проверяет базовую корректность конфигурации, включая отсутствие циклов в графе зависимостей и уникальность типов фактов и результатов. Во время запуска драйвер передаёт анализатору Pass: в нём есть синтаксические деревья и типовая информация пакета, результаты требуемых анализаторов и функции для отправки диагностик.

Для масштабируемой межпакетной проверки API вводит Facts, сериализуемые сведения, которые анализатор связывает с объектами или пакетом и затем импортирует в зависимых пакетах. Драйвер обеспечивает вычисление фактов зависимостей заранее и переносит их между пакетами, при необходимости через разные адресные пространства; для сериализации требуется gob-кодирование. Это позволяет расширять возможности последующих анализаторов, например предоставляя граф потока управления, обход синтаксических деревьев или промежуточное представление SSA, без жёсткой зависимости базового API от этих расширений.

Ключевые факты

  • go/analysis определяет интерфейс между статическим анализатором и драйвером, который запускает набор анализаторов и выводит их диагностику.
  • Модульный подход позволяет анализировать пакеты по отдельности и передавать сведения из зависимостей в вышележащие пакеты.
  • Тип Analyzer описывает имя, документацию, параметры, функцию Run, результаты и зависимости анализатора; Validate проверяет его базовую корректность.
  • Через Pass анализатор получает синтаксические деревья, сведения о типах, результаты зависимых проверок и функции Report и Reportf для диагностик.
  • Facts дают механизм для сериализуемых межпакетных сведений; драйвер распространяет их и использует gob-кодирование.

Почему это важно

Фреймворк даёт единый способ создавать и сочетать проверки Go-кода. Благодаря модульности проверки могут использовать результаты анализа зависимостей и при этом сохранять преимущества раздельной компиляции: повторно проверять только изменившиеся части и выполнять независимые части параллельно.

Кому это важно

В первую очередь разработчикам инструментов для Go: авторам проверщиков, драйверов наподобие vet, расширений для IDE и редакторов, интеграций для сборки, тестирования и код-ревью. Пользователи таких инструментов получают возможность настраивать вывод и приоритет диагностик на стороне драйвера.

Как это применить

Нужно объявить значение типа Analyzer с именем, документацией и функцией Run, а затем добавить этот Analyzer в список драйвера. Если проверке нужны результаты других анализаторов, их указывают в Requires и получают через Pass.ResultOf; для межпакетных сведений объявляют типы Facts и используют операции импорта и экспорта фактов из Pass.

Можно ли доверять

Это официальная документация пакета golang.org/x/tools/go/analysis. Она описывает назначение API, его основные типы и примеры использования, включая проверщик printf и интеграцию анализаторов в драйвер.

Риски и подводные камни

Драйвер отвечает за то, как показывать и приоритизировать диагностики: у структуры Diagnostic нет поля серьёзности. Анализатор, работающий с фактами, должен объявить сериализуемые типы фактов; кодирование должно быть детерминированным, иначе системы сборки с кэшами по содержимому могут получать лишние промахи кэша. Документация также предупреждает, что некоторые драйверы не запускают анализаторы для пакетов стандартной библиотеки, поэтому на факты оттуда нельзя безусловно полагаться.

«Статический анализ, это функция, которая исследует пакет Go-кода и сообщает набор диагностик, обычно об ошибках в коде, а также, возможно, выдаёт другие результаты, такие как предлагаемые рефакторинги или дополнительные факты.»

— Документация go/analysis