﻿# Безболезненное внедрение статического анализа и победа над ложными срабатываниями

Поговорим о том, как механизм baseline может упростить внедрение статического анализатора в проект, а также о том, как бороться с ложноположительными срабатываниями\.

![1272_baseline_ru/image1.png](https://import.viva64.com/docx/blog/1272_baseline_ru/image1.png)

## Внедрение анализатора в проект и baseline

Довольно частой проблемой при внедрении статического анализатора в уже существующий проект является то, что внедряется он не на "этапе котлована"\. Поэтому после первого запуска анализатор ругается на написанный ранее код\. И если кодовая база проекта достаточно обширная, то срабатываний будет действительно много:

![1272_baseline_ru/image2.png](https://import.viva64.com/docx/blog/1272_baseline_ru/image2.png)

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

Что же делать в таком случае?

Для начала мы должны понимать, что перед использованием статический анализатор нужно настраивать\. В анализаторе PVS\-Studio, например, сейчас более 1500 диагностических правил суммарно для четырёх языков\. Понятное дело, что не все эти диагностики могут быть вам полезны\.

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


> \*\*Примечание\\\.\*\* Если вам интересны стандарты MISRA, то у нас есть \[страница на сайте\]\(https://pvs\-studio\.ru/ru/pvs\-studio/sast/misra/\), посвящённая их поддержке в анализаторе PVS\\\-Studio\\\.

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

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

В статическом анализаторе PVS\-Studio для таких случаев есть режим suppress, в котором анализатор сохраняет подавленные сообщения в так называемые suppress\-файлы\. Этот режим не требует внесения изменений в исходный код, а также удобен для массового подавления срабатываний\. Анализатор сохраняет сигнатуру срабатывания, в том числе и исходный код, на который оно было выдано, чтобы в будущем без ведома пользователя срабатывание не вернулось в отчёт\.

Как же подавить срабатывания анализатора? Например, в плагине PVS\-Studio для интегрированной среды разработки Visual Studio есть специальная кнопка в меню просмотра отчёта, с помощью которой можно подавить все срабатывания анализатора:

![1272_baseline_ru/image3.png](https://import.viva64.com/docx/blog/1272_baseline_ru/image3.png)

После нажатия анализатор запишет все срабатывания в основной suppress\-файл проекта, при этом они пропадут из окна просмотра отчёта\.

Также есть возможность подавления конкретных срабатываний с помощью выпадающего меню:

![1272_baseline_ru/image4.png](https://import.viva64.com/docx/blog/1272_baseline_ru/image4.png)

Подобная функциональность присутствует во всех плагинах PVS\-Studio для интегрированных сред разработки: IntelliJ IDEA, CLion, Rider, Visual Studio, Visual Studio Code и Qt Creator\.

В плагине для Visual Studio с помощью специального меню **Suppress Messages** плагина можно посмотреть, какие срабатывания были подавлены:

![1272_baseline_ru/image5.png](https://import.viva64.com/docx/blog/1272_baseline_ru/image5.png)


> \*\*Примечание\\\.\*\* Подробнее о подавлении срабатываний в плагинах для интегрированных сред разработки и для других интеграций анализатора можно прочитать в \[соответствующем разделе\]\(https://pvs\-studio\.ru/ru/docs/manual/0032/\) нашей документации\\\.

## Подавление ложноположительных срабатываний

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


> \*\*Примечание\\\.\*\* Иногда вместо борьбы с ложными срабатываниями лучшим решением будет просто отключить анализ конкретных файлов\\\. Например, для автогенерируемых файлов анализатор может выдать много различных срабатываний, убрав которые, мы избавимся от лишнего шума в отчёте\\\.

Подавить ложное срабатывание в анализаторе PVS\-Studio можно с помощью специальных комментариев\. Допустим, в таком фрагменте анализатор выдал срабатывание:

```cpp
size_t n = 100;
for (unsigned i = 0;
     i < n;          // здесь получаем V104
     i++)
{
    // ...
}
```

Если мы уверены, что это срабатывание ошибочное и готовы убрать его из отчёта, необходимо добавить вот такой комментарий с номером диагностического правила:

```cpp
size_t n = 100;
for (unsigned i = 0;
     i < n; //-V104
     i++)
{
    // ...
}
```

Если необходимо подавить срабатывания сразу нескольких диагностик на одну и ту же строку, то делается это столь же просто:

```cpp
struct Small { int *pointer; };
struct Big { int *array[20]; };
int Add(const Small &a, Big b) //-V835 //-V813
{
  return *a.pointer + *b.array[10];
}
```

Также можно ставить дополнительный хэш\-код к метке **False Alarm**\. При изменении строки, в которой мы установим такой хэш\-код, выданные на неё предупреждения анализатора не будут отмечены как ложные срабатывания, так как хэш\-код изменённой строки будет отличаться от кода из метки\.

Для включения этой функции необходимо добавить в файл конфигурации правил `.pvsconfig` следующий флаг:

```cpp
//V_ENABLE_FALSE_ALARMS_WITH_HASH
```

В коде метка с хэш\-кодом выглядит следующим образом:

```cpp
//-V817 //-VH"3652460326"
```


> \*\*Примечание\\\.\*\* Об этом режиме и других возможностях файлов конфигурации диагностик можно прочитать \[в соответствующем разделе\]\(https://pvs\-studio\.ru/ru/docs/manual/6630/\) нашей документации\\\.



Более сложная ситуация возникает с подавлением срабатываний в макросах\. Ведь анализатор будет выдавать срабатывания в тех местах, где макрос используется, то есть там, где происходит подстановка его тела в код:

```cpp
#define TEST_MACRO \
  int a = 0;       \
  size_t b = 0;    \
  b = a; 

void func1()
{
  TEST_MACRO // здесь V1001
}

void func2()
{
  TEST_MACRO // и здесь V1001 
}
```

В таком случае можно воспользоваться специальной пометкой, которая укажет анализатору, что во всех случаях использования этого макроса срабатывания являются ложноположительными:

```cpp
//-V:TEST_MACRO:1001

#define TEST_MACRO \
  int a = 0;       \
  size_t b = 0;    \
  b = a; 

void func1()
{
  TEST_MACRO
}

void func2()
{
  TEST_MACRO
}
```

Также можно просто убрать эти срабатывания\. Для этого нужно оставить вот такой комментарий:

```cpp
//-V::1001:TEST_MACRO
....
```

Как и в прошлом примере, для макросов возможно указать сразу несколько диагностических правил:

```cpp
//-V:TEST_MACRO:1001, 105, 201
```

Вариант с расстановкой комментариев в исходном коде вручную может быть не самым удобным, поэтому плагины PVS\-Studio для интегрированных сред разработки позволяют упростить этот процесс до нажатия всего одной кнопки **Mark selected messages as False Alarms** в контекстном меню:

![1272_baseline_ru/image6.png](https://import.viva64.com/docx/blog/1272_baseline_ru/image6.png)

После нажатия в исходном коде комментарий о ложном срабатывании проставится автоматически:

![1272_baseline_ru/image7.png](https://import.viva64.com/docx/blog/1272_baseline_ru/image7.png)

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

Подобная возможность, как и массовое подавление срабатываний, доступна во всех плагинах PVS\-Studio для интегрированных сред разработки\.


> \*\*Примечание\\\.\*\* Подробнее об этих и других методах борьбы с ложноположительными срабатываниями можно прочитать в \[соответствующем разделе\]\(https://pvs\-studio\.ru/ru/docs/manual/0017/\) нашей документации\\\.

Также не стесняйтесь сообщать нам в техническую поддержку о ложных срабатываниях\. Для нас важно качество работы анализатора, и мы занимаемся исправлением возникающих ложных срабатываний, если это возможно\. Связаться с нами можно с помощью специальной [формы обратной связи](https://pvs-studio.ru/ru/about-feedback/) на сайте\.

### Отключение диагностических правил на фрагменте кода

Иногда нужно отключить диагностику, причём не глобально, а в конкретном блоке кода\. Для этого анализатор PVS\-Studio для C и C\+\+ проектов поддерживает специальные директивы `#pragma`:

* `#pragma pvs(push)` — сохраняет текущие настройки включения/отключения диагностик;
* `#pragma pvs(disable: XXXX, YYYY, ...)` — выключает диагностики с номерами из списка;
* `#pragma pvs(enable: XXXX, YYYY, ...)` — включает диагностики с номерами из списка;
* `#pragma pvs(pop)` — восстанавливает предыдущие настройки\.

Пример:

```cpp
void func(int* p1, int* p2)
{
  if (!p1 || !p2)
    return;

#pragma pvs(push)
#pragma pvs(disable: 547)
  if (p1) // V547 не будет выдана
    do_something();
#pragma pvs(pop)

  if (p2) // V547 Expression 'p2' is always true.
    do_other();
}
```

Чтобы избежать предупреждений о неизвестных `#pragma`, передайте компилятору флаг:

* GCC/Clang: `-Wno-unknown-pragmas`
* MSVC: `-wd4068`

## Заключение

Мы поговорили о том, как внедрить статический анализатор в уже существующий проект без лишней головной боли с помощью механизма baseline, который позволяет игнорировать старые срабатывания и сосредоточиться на новых\. Также мы обсудили, что анализатор не идеален и может выдавать ложноположительные предупреждения, но в PVS\-Studio для этого предусмотрены удобные способы подавления срабатываний как массово, так и локально\.

Если ранее вы сомневались в том, стоит ли внедрять статический анализатор в свой проект, вы всегда можете попробовать статический анализатор PVS\-Studio на своём коде, получив [бесплатную лицензию](https://pvs-studio.ru/ru/pvs-studio/try-free/)\. Переделывая известную поговорку: "Лучше один раз попробовать, чем сто раз услышать или прочитать"\.

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

* [Ложноположительные срабатывания статического анализатора кода](https://pvs-studio.ru/ru/blog/terms/6461/)
* Андрей Карпов\. [Как внедрить статический анализатор кода в legacy проект и не демотивировать команду](https://pvs-studio.ru/ru/blog/posts/0743/)
* Иван Пономарев\. [Внедряйте статический анализ в процесс, а не ищите с его помощью баги](https://habr.com/ru/articles/436868/)