Пользователи иногда спрашивают, как появляются новые диагностики в статическом анализаторе PVS-Studio. Мы отвечаем, что черпаем вдохновение из разных источников: книг, стандартов кодирования, собственных ошибок, электронных писем наших пользователей и других. Недавно нам пришла в голову интересная идея новой диагностики. Сегодня мы решили рассказать историю о том, как это было.

Все началось с обзора проекта COVID-19 CovidSim Model и статьи о неинициализированной переменной. Проект получился небольшим и написан с использованием современного стандарта языка C++. Это значит, что он может отлично пополнить базу тестовых проектов для регрессионного тестирования ядра анализатора PVS-Studio.

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

К счастью, разработчик, которому поручили добавить проект в тестовую базу, подошёл к задаче основательно и решил заглянуть в раздел диагностики MISRA. Это не было обязательным шагом. Диагностика MISRA обычно специфична. Их можно смело отключать для таких проектов, как CovidSim.

Диагностика MISRA C и MISRA C++ предназначена для разработчиков встраиваемых систем, и их задача — ограничить использование небезопасных программных конструкций. Например, не рекомендуется использовать оператор goto (V2502), так как он провоцирует создание сложного кода, в котором легко допустить логическую ошибку. Подробнее о философии стандарта кодирования MISRA читайте в статье Что такое MISRA и как это приготовить

Что касается разработки прикладного ПО, то включать их не имеет смысла. Проект CovidSim мог бы обойтись и без них. В противном случае пользователь просто утонет в огромном количестве малополезных в данном случае сообщений. Например, экспериментируя с этим набором диагностик, мы получили более миллиона предупреждений для некоторых средних открытых проектов. Грубо говоря, каждая третья строка кода может быть ошибочной с точки зрения MISRA. Никто не будет перебирать все предупреждения, а тем более исправлять их. Проект либо разрабатывается сразу с учетом рекомендаций MISRA, либо данный стандарт кодирования для него не актуален.

Впрочем, вернемся к теме. Итак, просматривая предупреждения MISRA, коллега мельком увидел предупреждение V2507, выданное для этого фрагмента кода.

if (radiusSquared > StateT[tn].maxRad2) StateT[tn].maxRad2 = radiusSquared;
{
  SusceptibleToLatent(a->pcell);
  if (a->listpos < Cells[a->pcell].S)
  {
    UpdateCell(Cells[a->pcell].susceptible, a->listpos, Cells[a->pcell].S);
    a->listpos = Cells[a->pcell].S;
    Cells[a->pcell].latent[0] = ai;
  }
}
StateT[tn].cumI_keyworker[a->keyworker]++;

Правило V2507 заставляет нас заключать тела условных операторов в фигурные скобки.

Сначала наш дотошный коллега подумал, что анализатор вышел из строя. Ведь в фигурных скобках блок текста! Это ложное срабатывание?

Давайте посмотрим поближе. Код только кажется правильным, но это не так! Фигурные скобки не присоединяются к оператору if.

Поправим код для ясности:

if (radiusSquared > StateT[tn].maxRad2)
  StateT[tn].maxRad2 = radiusSquared;
{
  SusceptibleToLatent(a->pcell);
  ....
}

Согласитесь, это приятный баг. Это наверняка войдет в топ-10 ошибок C++, которые мы нашли в 2021 году.

Что из этого следует? Стандартный подход MISRA работает! Да, это заставляет вас везде писать фигурные скобки. Да, это утомительно. Хотя это разумная цена за повышение надежности встроенных приложений, используемых в медицинских устройствах, автомобилях и других высокоответственных системах.

Я рад, что у разработчиков, использующих стандарт MISRA, все в порядке. Однако рекомендовать всем использовать фигурные скобки — плохая идея. При таком подходе очень легко довести анализатор до состояния, когда его использование становится невозможным. Предупреждений будет так много, что никому до них не будет дела.

Наконец мы пришли к идее новой диагностики общего анализа и следующего правила.

Анализатор выдаст предупреждение в случае выполнения следующих условий для оператора if:

  • весь условный оператор if записывается в одну строку и имеет только ветвь then;
  • следующий оператор после if является составным оператором и находится в разных строках с if.

Мы с нетерпением ждем получения достойного правила, которое дает мало ложных срабатываний.

Вот как эта идея теперь описана в нашем таск-трекере. Возможно, в процессе реализации что-то будет сделано по-другому, но на данном этапе это не имеет большого значения. Главное, появится достойное диагностическое правило, которое начнет выявлять новый паттерн ошибки. Далее мы расширим его на ядра C# и Java анализатора PVS-Studio.

Мы только что рассмотрели уникальный пример того, как появилось новое диагностическое правило, которое мы будем реализовывать в PVS-Studio. Спасибо проекту CovidSim, стандарту кодирования MISRA и наблюдательности нашего коллеги.

Спасибо за внимание и следуйте за мной в мир C++ и багов :). Твиттер. Фейсбук.

Дополнительные ссылки: