Анонсируем новый линтер (статический анализатор) для Go, который одновременно является песочницей для прототипирования ваших задумок в мире статического анализа. go-critic построен вокруг следующих наблюдений: В этом посте мы рассмотрим использование и архитектуру go-critic, некоторые реализованные в нём проверки, а также опишем основные шаги добавления своей функции-анализатора в него. (Форматирование предупреждений отредактировано, оригиналы доступны в gist'е.) Утилита gocritic умеет проверять отдельные пакеты по их import пути (check-package), а так же рекурсивно обходить все директории (check-project). Например, вы можете проверить весь $GOROOT или $GOPATH с помощью одной команды: Есть поддержка “белого списка” для проверок, чтобы явно перечислить какие проверки нужно выполнять (флаг -enable). По умолчанию запускаются все те проверки, которые не отмечены значком Experimental или VeryOpinionated. Планируются интеграции в golangci-lint и gometalinter. Проводя очередное код-ревью Go проекта, или при аудите некоторой 3-rd party библиотеки, вы можете раз за разом замечать одни и те же проблемы. К вашему сожалению, найти линтер, который диагностировал бы этот класс проблем, не удалось. Первым вашим действием может быть попытка категоризировать проблему и связаться с авторами существующих линтеров, предлагая им добавить новую проверку. Шансы на то, что ваше предложение примут, сильно зависит от проекта и могут быть довольно низкими. Далее, скорее всего, последуют месяцы ожидания. А что, если проверка и вовсе неоднозначная и может быть кем-то воспринята как слишком субъективная или недостаточно точная? Может, есть смысл попробовать написать эту проверку самому? go-critic существует для того, чтобы стать домом для экспериментальных проверок, которые проще реализовать самим, чем пристроить их в существующие статические анализаторы. Само устройство go-critic минимизирует количество контекста и действий, которые необходимы для добавления новой проверки — можно сказать, что потребуется добавить только один файл (не считая тестов). Критик — это набор правил (rules), которые описывают свойства проверки и микро-линтеры (checkers), реализующие инспекцию кода на соответствие правилу. Приложение, которое встраивает линтер (например, cmd/gocritic или golangci-lint), получает список поддерживаемых правил, фильтрует их определённых образом, создаёт для каждого отобранного правила check функцию, и запускает каждую из них над исследуемым пакетом. Работа по добавлению нового checker’а сводится к трём основным шагам: Пройдём все эти пункты на примере правила captLocal, которое требует отсутствия локальных имён, начинающихся с заглавной буквы.  Для добавления тестовых данных для новой проверки, нужно создать новую директорию в lint/testdata. Каждая такая директория должна иметь файл positive_tests.go, который описывает примеры кода, на которых проверка должна срабатывать. Чтобы тестировать отсутствие ложных срабатываний, тесты дополняются “корректным” кодом, в котором новая проверка не должна находить никаких проблем (negative_tests.go). Примеры: Запустить тесты можно после добавления нового линтера. Создадим файл с названием чекера: lint/captLocal_checker.go.
По конвенции, все файлы с микро-линтерами имеют суффикс _checker. checkerBase — это тип, который должен быть встроен (embedded) в каждый checker.
Он предоставляет реализации по умолчанию, что позволяет писать меньше кода в каждом линтере.
Помимо прочего, checkerBase включает указатель на lint.context, который содержит информацию о типах и другие метаданные о проверяемом файле. Поле upcaseNames будет содержать таблицу известных имён, которые мы будем предлагать заменять на strings.ToLower(name) версию. Для тех имён, которые не содержатся в мапе, будет предлагаться не использовать заглавную букву, но корректной замены предоставляться не будет. Внутреннее состояние инициализируется единожды для каждого экземпляра.
Метод Init() требуется определять только для тех линтеров, которым нужно провести предварительную инициализацию. Теперь требуется определить саму проверяющую функцию.
В случае captLocal, нам нужно проверять все локальные ast.Ident, которые вводят новые переменные. Для того, чтобы проверять все локальные определения имён, следует в своём чекере реализовать метод со следующей сигнатурой: Список доступных visitor интерфейсов может быть найден в файле lint/internal/visitor.go.
captLocal реализует LocalDefVisitor. По конвенции, методы, которые генерируют предупреждения, обычно выносятся в отдельные методы. Есть редкие исключения, но следование этому правилу считается хорошей практикой. Ещё одним необходимым к реализации методом является InitDocumentation: Обычно, достаточно заполнить 3 поля: Повторно генерировать документацию не является обязательным требованием для нового линтера, возможно в ближайшем будущем этот шаг будет полностью автоматизирован. Но если вы всё же хотите проверить, как будет выглядеть выходной markdown файл, воспользуйтесь командой make docs. Файл docs/overview.md будет обновлён. Последним штрихом является регистрация нового линтера: addChecker ожидает указатель на zero-value нового линтера. Далее идёт вариадичный аргумент, позволяющий передать ноль или несколько аттрибутов, описывающих свойства реализации правила. attrSyntaxOnly — опциональный маркер для линтеров, которые не пользуются информацией о типах в своей реализации, что позволяет запускать их без выполнения проверки на типы. golangci-lint отмечает такие линтеры флагом “fast” (потому что они выполняются гораздо быстрее). attrExperimental — атрибут, присваиваемый всем новым реализациям. Удаление этого атрибута возможно только после стабилизации реализуемой проверки. Теперь, когда новый линтер зарегистрирован через addChecker, можно запустить тесты: При рассматривании pull requests мы стараемся придерживаться стратегии optimistic merging. В основном это выражается в принятии тех PR, к которым у проводящего ревью могут оставаться некоторые, в частности чисто субъективные, претензии. Сразу после вливания такого патча может последовать PR от ревьювера, который исправляет эти недочёты, в CC (copy) добавляется автор исходного патча. У нас также есть два маркера линтеров, которые могут быть использованы во избежание красных флагов в случае отсутствия полного консенсуса: Experimental — потенциально временное и исправимое свойство реализации. VeryOpinionated — это более фундаментальное свойство правила, которое не зависит от реализации. Рекомендуется создавать  тикет на github’е перед тем, как отправлять реализацию, но если вы уже отправили pull request, открыть соответствующий issue могут за вас. Подробнее о деталях процесса разработки смотри в CONTRIBUTING.md.
Основные правила перечислены в секции main rules. Вы можете участвовать в проекте не только добавлением новых линтеров.
Есть множество других путей: go-critic критикует ваш Go код голосами всех программистов, участвующих в его разработке. Критиковать может каждый, поэтому — присоединяйтесь!  Making developer tools & video games