Вы пользуетесь GitHub, пишете код и делаете прочие веселые штуки. Для повышения качества своей работы и оптимизации своего времени используете статический анализатор. И вот вам приходит идея - а почему бы не смотреть на ошибки, которые выдал анализатор, прямо в GitHub? Да и еще, чтобы это красиво выглядело. Что же делать в этом случае? Ответ очень простой. Ваш выбор – SARIF. О том что это такое, как это настроить, и будет рассказано в данной статье. Приятного чтения.

Что такое SARIF?

SARIF (Static Analysis Results Interchange Format) – это формат обмена результатами статического анализа на основе JSON для вывода инструментов статического анализа. То есть нам достаточно получить отчет анализатора в этом формате, и далее мы можем его использовать в тех продуктах, которые поддерживают данный формат - например, на GitHub или в Visual Studio Code.

Этот формат появился из-за того, что в мире инструментов статического анализа каждый создавал свой собственный выходной формат. Однако, даже если отчёты разных анализаторов представлены в одном и том же формате (например, JSON), структура самих отчетов может кардинально отличаться друг от друга. Поэтому один общий стандарт был лишь вопросом времени.

Данный формат (SARIF) быстро развивается, и его начинают использовать все чаще и чаще. Однако на данный момент у него есть небольшой недостаток. Иногда он меняет свою структуру, и приходится немного корректировать код, чтобы SARIF файл проходил валидацию. Однако это мелочи по сравнению с тем, какую пользу он несёт. В теории, в идеальном мире, достаточно получить отчет в этом формате и далее его можно открыть в любой программе\системе, которая работает с результатами статического анализа. Ну красота же!

Настройка репозитория GitHub

Чтобы GitHub начал анализировать SARIF файлы, сначала необходимо настроить репозиторий. При настройке мы пользовались вот этой инструкцией.

Итак, открываем свой репозиторий и нажимаем на "Security".

Находим по центру "Code scanning alerts" и нажимаем справа на кнопку "Set up code scanning".

Далее нажимаем на "Set up this workflow".

Теперь даем имя для yml файла (например upload-sarif.yml) и пишем следующее содержимое:

name: "Upload SARIF"

# Run workflow each time code is pushed to your repository and on a schedule.
# The scheduled workflow runs every at 00:00 on Sunday UTC time.
on:
  push:
  schedule:
  - cron: '0 0 * * 0'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    # This step checks out a copy of your repository.
    - name: Checkout repository
      uses: actions/checkout@v2
    - name: Upload SARIF file
      uses: github/codeql-action/upload-sarif@v1
      with:
        # Path to SARIF file relative to the root of the repository
        sarif_file: results.sarif

Выглядеть это должно следующим образом:

Теперь нажимаем "Start commit", пишем какое-то сообщение (например "Create upload-sarif.yml") и коммитим.

Отлично, мы настроили репозиторий! Можно приступать к получению SARIF файла.

Получение SARIF файла

Как вы поняли, SARIF – унифицированный стандарт, и получить его можно с помощью разных статических анализаторов и инструментов. В этой статье мы будем использовать PVS-Studio и PlogConverter. Обо всем об этом – далее.

Проверяем проект

Чтобы получить SARIF файл, сначала нам необходимо проверить проект с помощью статического анализатора. Поэтому в настроенный выше репозиторий мы добавили небольшой тестовый С++ проект с одним файлом для демонстрации. Ибо что мы будет проверять-то в самом деле? :) Вот содержимое файла:

#include <iostream>
void f(unsigned int ch) 
{
  unsigned int chx = -1;
  if (ch >= 0x0fff0)
  {
    if ( !((ch >= 0x0FF10) && (ch <= 0x0FF19)) 
       || ((ch >= 0x0FF21) && (ch <= 0x0FF3A)) 
       || ((ch >= 0x0FF41) && (ch <= 0x0FF5A)))
    {
      ch = chx;
    }
  }
}
int main()
{
  std::cout << "error" << std::endl;
}

Кстати, синтетический пример с ошибкой имеет реальный прототип, описанный в статье "Как PVS-Studio оказался внимательнее, чем три с половиной программиста".

Как уже было упомянуто выше, проверка будет осуществляться с помощью статического анализатора PVS-Studio. А именно - с помощью консольной утилиты "PVS-Studio_Cmd.exe". Данная утилита позволяет производить анализ C++, C# MSBuild-проектов на Windows. По умолчанию найти ее можно по пути "C:\Program Files (x86)\PVS-Studio". Подробнее о данной утилите Вы можете почитать здесь.

Если у вас нет лицензии для проверки, то не отчаивайтесь. Кликнув сюда, вы перейдёте на сайт анализатора, где сможете скачать его, а также получить триальную лицензию.

Собственно, приступаем к анализу. Чтобы его произвести достаточно выполнить вот эту команду:

PVS-Studio_Cmd.exe -t "D:\Use_SARIF_Example\BestProjectCpp.sln" \
-o "D:\Use_SARIF_Example\results.plog" -e "D:\Use_SARIF_Example\"

Рассмотрим строку запуска немного подробнее. Флаг "-t" является обязательным. Он позволяет указать объект для проверки (sln или csproj/vcxproj файл). Флаг "-o" отвечает за путь до файла, в который будут записаны результаты анализа. Флаг "-e" - корневая часть пути, которую PVS-Studio будет использовать при генерации относительных путей в предупреждениях. Нужно это потому, что отчет будет обрабатываться в облаке.

Отлично, теперь нужно преобразовать plog файл в SARIF файл. Для этого воспользуемся утилитой PlogConverter.

Преобразование из Plog в SARIF

Преобразование мы будем выполнять с помощью утилиты PlogConverter, поэтому пару слов о ней. PlogConverter – это утилита с открытым исходным кодом, предназначенная для преобразования отчетов анализатора PVS-Studio из одного формата в другой. Более подробно утилита описана в документации.

Итак, нам необходимо найти PlogConverter.exe на компьютере. Эта утилита устанавливается вместе с PVS-Studio и располагается рядом с "PVS-Studio_Cmd.exe". Переходим туда, открываем консоль и пишем следующую команду:

PlogConverter.exe "D:\Use_SARIF_Example\results.plog" \
-o "D:\Use_SARIF_Example" -t sarif -n results

Вот и все. Теперь можно заливать этот файл и смотреть результаты анализа.

Проверяем, что все работает

Чтобы проверить, что мы все сделали правильно, быстренько загрузим наш SARIF файл руками и посмотрим результаты анализа. Для этого переходим в репозиторий и нажимаем "Add file -> Upload files".

Далее добавляем SARIF файл и ждем, пока он обработается. Если хочется посмотреть, как там проходит обработка, то необходимо нажать на "Actions" и далее выбрать работающую задачу.

Как только все будет готово, заходим во вкладку "Security". В ней выбираем слева "Code scanning alerts -> PVS-Studio".

Справа у нас и будут сообщения анализатора. Откроем какое-нибудь предупреждение:

Мы видим:

  1. Быстрый фильтр по ошибкам;

  2. Сообщение об ошибке. Оно же и указывает, где именно в исходном коде находится ошибка;

  3. Ссылка на документацию для предупреждения анализатора.

Реальный сценарий использования SARIF в GitHub

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

#include <iostream>
void f(unsigned int ch) 
{
  unsigned int chx = -1;
  if (ch >= 0x0fff0)
  {
    if (!((ch >= 0x0FF10) && (ch <= 0x0FF19)) 
      || ((ch >= 0x0FF21) && (ch <= 0x0FF3A)) 
      || ((ch >= 0x0FF41) && (ch <= 0x0FF5A)))
    {
      ch = chx;
    }
  }
}

int ComputeProjectionMatrixFOV(float fov)
{
  float yScale = 1.0 / tan((3.141592538 / 180.0) * fov / 2);
  return yScale;
}

int main()
{
  std::cout << "error" << std::endl;
}

Далее проверяем файл, сохраняем отчет, получаем SARIF файл (заменяя им уже лежащий в скаченном репозитории) и делаем коммит в отдельную ветку. Все, пользователь сделал свое дело. Теперь черед смотреть ошибки.

Заходим в репозиторий. Далее "Security" -> "Code scanning alerts" -> "PVS-Studio" и справа в "Branch" выбираем нужную ветку. Смотрим результаты:

Как вы видите, сообщения об ошибках в ветке хранятся отдельно. Согласитесь, это достаточно удобно. При желании можно, например, создать bat файл, который будет запускать анализатор, конвертировать отчет в SARIF и заменять существующий SARIF файл.

Что можно делать результатами?

Итак, вы остались один на один с отчетом. Что доступно в вашем распоряжении? Первое, на что нужно обратить внимание, — это то, что все ошибки разделены на две группы. Это "Open" и "Closed". "Open" — это активные ошибки, которые мы не обработали. "Closed" – это ошибки, которые мы исправили или пометили как ложные.

Второе — это фильтры по статусам ошибок (закрытые, отрытые и все такое).

Еще есть фильтры по характеристикам ошибок. Например, можно отсортировать по номеру ошибки.

Также GitHub позволяет нам помечать сообщения как "false positive", "used in tests", и мое любимое "won't fix" :). Чтобы пометить сообщение, необходимо его выбрать (слева от сообщения есть checkbox) и справа вверху нажать на "Dismiss".

Сообщения, которые вы так разметите, не будут попадать в графу открытых ошибок при следующей загрузке SARIF файла.

Если у нас появилась необходимость вернуть сообщения в "Open", то это очень легко сделать. Для этого выбираем "Closed", далее выбираем то, что хотим вернуть, и нажимаем справа "Reopen".

Также обратите внимание, что если загрузить новый лог, то он перезаписывает текущие открытые ошибки. Если ошибки, которые были в "Open", не встретились в новом логе, то они попадают в "Closed". Поэтому рекомендуем использовать SARIF только для анализа всего проекта. Если вам требуется проанализировать только pull request, то для этого у нас есть ряд статей на эту тему. Например, вот эта. Использовать же SARIF для анализа pull request будет не очень удобно.

А работает только для C++?

Конечно же нет, вы вообще не зависите от языка. Все, что вам нужно – это инструмент статического анализа, который сможет проанализировать ваш код и создать SARIF файл. Например, используемый в этой статье PVS-Studio умеет анализировать C++, C#, Java. Поэтому давайте еще попробуем проверить код на C#, потому что это лучший язык в мире один из авторов статьи его любит. Для примера быстренько проделываем все то же самое, о чем говорилось в статье, но уже для C# проекта. Вот содержимое файла, который был проанализирован:

using System;
using System.Collections.Generic;
using System.Linq;

namespace TestSarif
{
  class Program
  {
    static void Main()
    {
      var result = Formula42(3, 5);
    }

    static int Formula42(int? coefficientA, int? coefficientB)
    {
      var data = new List<int>();
      if (coefficientA != null)
        data.Add(Formula42(coefficientA.Value));
      else if (coefficientB != null)
        data.Add(Formula42(coefficientA.Value));
      return data.SingleOrDefault();
    }

    static private int Formula42(int coefficient)
    {
      return coefficient;
    }
  }
}

Вот результат:

Ну и посмотрим на саму ошибку.

Вывод

Подводя итог, хочется сказать, что SARIF — это удобный формат, который позволяет просматривать результаты анализа. Да и настройка использования SARIF быстрая и простая. Например, в VS Code это вообще делается в пару кликов. Кстати, если вам будет интересно, как это сделать, то напишите об этом в комментариях. Да и вообще, если у вас есть какие-то пожелания по темам статьи, то напишите об этом.

Так что пробуйте и пользуйтесь. Спасибо за внимание.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Nikolay Mironov, Evgeniy Ovsannikov. How to Get Nice Error Reports Using SARIF.

Комментарии (5)