Внедрение статического анализатора в проект для некоторых людей выглядит непреодолимой преградой. Почему-то очень распространено мнение, что объём выданных результатов анализа при первом запуске настолько велик, что рассматривается только два варианта: не связываться с этим или переключить всех людей на исправление предупреждений. В этой статьей попробуем развеять этот миф, проведя внедрение и настройку анализатора на проекте GTK.

Введение

GTK – кроссплатформенная библиотека элементов интерфейса. Недавно состоялся релиз GTK 4, что стало хорошим инфоповодом изучить качество кода проекта с помощью статического анализатора кода PVS-Studio. Такая деятельность для нас является регулярной, и нам часто приходится настраивать анализатор с нуля на многих проектах перед исследованием качества кода. В этой заметке я поделюсь опытом быстрой настройки PVS-Studio на C++ проекте.

Анализ GTK

Первые результаты

Получаем свой первый отчёт анализатора и видим следующие результаты для диагностик общего назначения:

4 (Fails) + 1102 (High) + 1159 (Medium) + 3093 (Low) = 5358 предупреждений.

Далее быстро прокручиваем отчёт, выявляем всплески неинтересных предупреждений и принимаем решение для дальнейшей настройки анализатора.

Исключаем директории

Рассмотрим такое предупреждение:

V530 [CWE-252] The return value of function 'g_strrstr_len' is required to be utilized. strfuncs.c 1803

/* Testing functions bounds */
static void
test_bounds (void)
{
  ....
  g_strrstr_len (string, 10000, "BUGS");
  g_strrstr_len (string, 10000, "B");
  g_strrstr_len (string, 10000, ".");
  g_strrstr_len (string, 10000, "");
  ....
}

Это код тестов, причём не относящихся непосредственно к GTK, поэтому составляем список директорий для исключения из анализа и перезапускаем PVS-Studio.

При следующем запуске из анализа будут исключены следующие директории:

gtk/_build/
gtk/subprojects/
gtk/tests/
gtk/testsuite/

Открываем отчёт и получаем следующий результат:

2 (Fails) + 819 (High) + 461 (Medium) + 1725 (Low) = 3007 предупреждений.

Ещё один положительный эффект, который мы получили после такой настройки, — это ускорение анализа.

Исключаем макросы

Макросы, пожалуй, - одна из основных причин аномального количества срабатываний у некоторых диагностик. Поверхностно смотрим наш отчёт и замечаем множество подобных предупреждений:

V501 There are identical sub-expressions '* (& pipe->ref_count)' to the left and to the right of the '^' operator. gdkpipeiostream.c 65

static GdkIOPipe *
gdk_io_pipe_ref (GdkIOPipe *pipe)
{
  g_atomic_int_inc (&pipe->ref_count);

  return pipe;
}

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

#V501
//-V:g_atomic_int_:501
#V547
//-V:GTK_IS_:547
//-V:GDK_IS_:547
//-V:G_IS_:547
//-V:G_VALUE_HOLDS:547
#V568
//-V:g_set_object:568

Всего несколько строчек, которые покрывают большинство проблемных макросов для V501, V547 и V568.

Смотрим результат:

2 (Fails) + 773 (High) + 417 (Medium) + 1725 (Low) = 2917 предупреждений.

Отключаем диагностики

Некоторые диагностики изначально выдают неподходящие предупреждения для конкретного проекта. Рассмотрим предупреждение V1042:

V1042 [CWE-1177] This file is marked with copyleft license, which requires you to open the derived source code. main.c 12

Это очень полезная диагностика для проектов с закрытым кодом, чтобы случайно не включить в них код с недопустимой лицензией. Но для GTK это неинтересная диагностика, поэтому отключим её и получим скорректированный результат:

2 (Fails) + 164 (High) + 417 (Medium) + 1725 (Low) = 2308 предупреждений.

Изучаем фейлы

В проекте имеются 2 предупреждения типа Fails:

  • V002 Some diagnostic messages may contain incorrect line number in this file. gdkrectangle.c 1

  • V002 Some diagnostic messages may contain incorrect line number in this file. gdktoplevelsize.c 1

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

Эти предупреждения можно просто проигнорировать.

Выводы

Итоговый результат такой:

164 (High) + 417 (Medium) + 1725 (Low) = 2306 предупреждений.

Наверняка, там ещё есть, что настроить, но я уже решил задачу, которую ставил перед собой: быстро получить отчёт анализатора, в котором легко найти ошибки. Например, предупреждение с номером V501 теперь всего одно во всём отчёте, и оно хорошее:

V501 There are identical sub-expressions 'G_PARAM_EXPLICIT_NOTIFY' to the left and to the right of the '|' operator. gtklistbase.c 1151

static void
gtk_list_base_class_init (GtkListBaseClass *klass)
{
  ....
  properties[PROP_ORIENTATION] =
    g_param_spec_enum ("orientation",
                       P_("Orientation"),
                       P_("The orientation of the orientable"),
                       GTK_TYPE_ORIENTATION,
                       GTK_ORIENTATION_VERTICAL,
                       G_PARAM_READWRITE |
                       G_PARAM_EXPLICIT_NOTIFY |  // <=
                       G_PARAM_EXPLICIT_NOTIFY);  // <=
  ....
}

Это отличный результат! И показатели других диагностик тоже значительно выросли. Мизерными настройками удалось уменьшить отчёт анализатора на целых 57%. Соответственно, показатель верных предупреждений к ложным тоже значительно вырос.

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

А теперь время передать эстафету моему коллеге Андрею Карпову.

Примечание Андрея Карпова

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

Конечно, моя задача проще и сильно отличается от процесса настройки и внедрения анализатора в реальный проект. Мне достаточно поверхностно пробежаться по списку предупреждений и выписать явные ошибки, игнорируя ложные срабатывания или непонятные предупреждения в сложных участках кода. При реальном использовании понадобится больше времени, чтобы настроить анализатор, точечно подавить ложные срабатывания, улучшить макросы и так далее. Но на самом деле, это не страшно. Например, в статье про проверку проекта EFL Core Libraries я показал, что можно достаточно легко настроить анализатор, чтобы он выдавал всего 10-15% ложных предупреждений. Согласитесь, неплохо, когда на каждые 1-2 ложных срабатывания вы исправляете 8-9 ошибок.

Ну и не забывайте, что вам всегда доступен механизм массового подавления срабатываний анализатора. Это позволяет начать быстро использовать анализатор даже в большом проекте. Все предупреждения объявляются техническим долгом и пока не показываются. И команда начинает работать только с предупреждениями, относящимися к новому или изменённому коду. Подробнее про это предлагаю почитать в статье "Как внедрить статический анализатор кода в legacy проект и не демотивировать команду".

Спасибо за внимание и приходите через пару недель читать статью про найденные ошибки.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Svyatoslav Razmyslov. GTK: The First Analyzer Run in Figures.

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