Подавление ложных предупреждений


В данном разделе описаны механизмы подавления предупреждений, выдаваемых анализатором. С помощью описанных здесь механизмов можно управлять как отдельными сообщениями, выдаваемыми на определённые строки кода, так и подавлять множественные срабатывания, возникающие, например, из-за использования C/C++ макросов. Описан способ, как с помощью комментариев указать анализатору выключить то или иное сообщение анализатора или изменить выдаваемый текст сообщения.

Механизмы, описанные в данном разделе, применимы как для C/C++, так и для C# анализаторов PVS-Studio, если явно не указано обратное.

Механизм подавления отдельных ложных срабатываний (Mark as False Alarm)

Любой анализатор кода всегда выдает помимо полезных сообщений об ошибках еще множество так называемых "ложных срабатываний". Это ситуации, когда программисту совершенно очевидно, что в коде нет ошибки, а анализатору это не очевидно. Такие ложные срабатывания называют False Alarm. Рассмотрим пример кода:

obj.specialFunc(obj);

Анализатор считает подозрительным, что у объекта вызывается метод, в качестве аргумента в который передаётся тот же самый объект, поэтому он выдаст на данный код предупреждение V678. Программист же может знать, что использование метода 'specialFunc' таким образом вполне допустимо, поэтому предупреждение анализатора в данном случае является ложным срабатыванием. О том, что предупреждение V678, выданное на этот код, является ложным, можно сообщить анализатору. Это можно сделать либо вручную, либо с помощью команды контекстного меню.

Чтобы подавить ложное срабатывание, в код можно добавить специальный комментарий:

obj.specialFunc(obj); //-V678

Теперь анализатор не будет выдавать предупреждение V678 на эту строку. После того, как сообщение будет отмечено как ложное срабатывание, оно исчезнет из списка. Для включения отображения размеченных подобным образом сообщений можно воспользоваться настройкой 'PVS-Studio -> Options... -> Specific Analyzer Settings -> DisplayFalseAlarms'

Комментарий, подавляющий предупреждения, можно вписать в код самостоятельно, но важно полностью соблюдать формат записи: две косые черты, минус (без пробела), код ошибки. Также можно воспользоваться специальной командной для этого, предоставляемой PVS-Studio. Пользователю предоставляется две команды, доступные из контекстного меню PVS-Studio (рисунок 1).

Рисунок 1 - Команды для работы с механизмом подавления ложных предупреждений

Рисунок 1 - Команды для работы с механизмом подавления ложных предупреждений

Рассмотрим доступные команды, относящиеся к подавлению ложных предупреждений:

1. Mark selected messages as False Alarms. Вы можете выбрать одно или несколько предупреждений в списке (рисунок 2) и воспользоваться этой командой для разметки соответствующего кода, как безопасного.

Рисунок 2 - Выбор предупреждений перед выполнением команды "Mark selected messages as False Alarms"

Рисунок 2 - Выбор предупреждений перед выполнением команды "Mark selected messages as False Alarms"

2. Remove False Alarm marks from selected messages. Убирает комментарий, помечающий код как безопасный. Функция, например, может быть полезна, если вы поспешили и ошибочно отметили код как безопасный. Как и в предыдущем случае, вы должны выбрать сообщения из списка, которые планируете обработать.

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

Реализация функции подавления ложных предупреждений

Обычно в компиляторах для подавления отдельных сообщений об ошибках используют #pragma-директивы. Приведем пример кода:

unsigned arraySize = n * sizeof(float);

Компилятор выдает сообщение:

warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned

int', possible loss of data x64Sample.cpp 151

Это сообщение можно подавить с помощью следующей конструкции:

#pragma warning (disable:4267)

Точнее, чтобы подавить конкретно это сообщение, лучше оформить код так:

#pragma warning(push)
#pragma warning (disable:4267) 
  unsigned arraySize = n * sizeof(float);
#pragma warning(pop)

Анализатор PVS-Studio в качестве разметки использует комментарии специального вида. Для той же строчки кода подавление сообщения PVS-Studio будет выглядеть так:

unsigned arraySize = n * sizeof(INT_PTR); //-V103

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

  size_t n = 100;
  for (unsigned i = 0;
       i < n; // анализатор сообщит о проблеме здесь
       i++)
  {
      // ...
  }

Чтобы подавить это сообщение при использовании комментария, достаточно написать:

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

Если же в это выражение пришлось бы добавлять #pragma-директиву, то код выглядел бы значительно менее наглядно.

Хранение разметки в исходном коде позволяет вносить в него модификации без опасения потерять информацию о строках с ошибками.

Можно также использовать отдельную базу, в которой хранить информацию примерно так: код ошибки, имя файла, номер строки. Данный подход отдельно реализован в PVS-Studio и называется "Mass Suppression".

Подавление ложных предупреждений в С/С++ макросах (#define) и для других фрагментов кода

В макросах (#define) анализатор также, разумеется, может находить потенциальные проблемы и выдавать на них диагностические сообщения. Но при этом анализатор будет выдавать сообщения в тех местах, где макрос используется, то есть где фактически происходит подстановка тела макроса в код. Пример:

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

void func1()
{
  TEST_MACRO // V101 here
}

void func2()
{
  TEST_MACRO // V101 here
}

Чтобы подавить это сообщение, можно использовать команду "Mark as False Alarm". Тогда код с расставленными командами подавления будет выглядеть так:

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

void func1()
{
  TEST_MACRO //-V101
}

void func2()
{
  TEST_MACRO //-V101
}

Однако, если макрос используется очень активно, то везде размечать его как False Alarm не очень удобно. Есть возможность в коде сделать вручную специальную пометку, чтобы анализатор автоматически размечал диагностики в этом макросе как False Alarm. С этой пометкой код будет выглядеть так:

//-V:TEST_MACRO:101

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

void func1()
{
  TEST_MACRO
}

void func2()
{
  TEST_MACRO
}

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

//-V:TEST_MACRO:101, 105, 201

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

#define NO_ERROR 0
#define VB_NODATA ((long)(77))
size_t stat;

#define CHECK_ERROR_STAT                        \
    if( stat != NO_ERROR &&  stat != VB_NODATA ) \
      return stat;

size_t testFunc()
{
    {
      CHECK_ERROR_STAT // #1
    }

    {
      CHECK_ERROR_STAT // #2
    }

    return VB_NODATA; // #3
}

В указанном примере диагностика V126 появляется в трех местах. Чтобы автоматически помечать ее как False Alarm в местах #1 и #2 нужно добавить такой код:

//-V:CHECK_ERROR_STAT:126

А чтобы и в #3 это сработало, необходимо указать еще:

//-V:VB_NODATA:126

К сожалению, просто указать "сразу помечать V126 в макросе VB_NODATA" и не указывать про макрос CHECK_ERROR_STAT нельзя из-за технических особенностей механизма препроцессирования.

Всё написанное в этом разделе про макросы справедливо также и для любого фрагмента кода. То есть если, например, вы хотите подавить все срабатывания диагностики V103 на вызов функции 'MyFunction', необходимо добавить такую строку:

//-V:MyFunction:103

Подавление ложных предупреждений с помощью файлов конфигурации диагностик (.pvsconfig)

Отображением и фильтрацией сообщений можно управлять с помощью комментариев специального вида. Такие комментарии можно писать в специальных файлах конфигурации (.pvsconfig) для всех анализаторов, либо непосредственно в коде проекта (только для C/C++ анализатора).

Файлы конфигурации диагностик анализатора представляют собой простые текстовые файлы, добавляемые в Visual Studio проект либо solution. Для добавления файла конфигурации, выделите интересующий вас проект или solution в окне Solution Explorer среды Visual Studio и выберите пункт контекстного меню 'Add New Item...'. В появившемся окне выберите тип файла 'PVS-Studio Filters File' (рисунок 3):

Рисунок 3 - Добавление в solution файла конфигурации диагностик анализатора.

Рисунок 3 - Добавление в solution файла конфигурации диагностик анализатора.

Из-за особенностей некоторых версий среды Visual Studio, тип файлов 'PVS-Studio Filters File' может отсутствовать на некоторых версиях и редакциях Visual Studio в окне добавления нового файла для solution и\или проекта. В таком случае, можно добавить в проект обычный текстовый файл, задав ему расширение 'pvsconfig'. В свойствах этого файла (после добавления), должно быть указано, что файл не участвует в сборке.

Файл конфигурации, добавленный в проект, действует на все файлы данного проекта. Файл конфигурации, добавленный в solution, действует на все файлы всех проектов, добавленных в данный solution.

Также можно разместить файл конфигурации .pvsconfig в текущей папке пользовательских данных (%AppData%\PVS-Studio\) - такой файл будет подхвачен автоматически при запуске проверки, без необходимости как-либо модифицировать проектные файлы.

Файлы конфигурации диагностик .pvsconfig имеют простой синтаксис. Любая строка, начинающаяся с символа '#' считается комментарием и игнорируется. Фильтры записываются в формате однострочных C++/C# комментариев, т.е. должны начинаться с символов '//'.

Для C/C++ кода, фильтры также могут быть записаны в виде комментариев непосредственно в исходном коде. Обратите внимание, что такой формат записи не поддерживается в C# проектах!

Далее рассмотрим различные варианты фильтров, допустимых в файлах конфигурации диагностик.

Фильтрация сообщений по фрагменту исходного кода (например, имена макросов, переменных и функций)

Предположим, есть следующая структура:

struct MYRGBA
{
  unsigned data;
};

И ряд функций, которые её используют:

void f1(const struct MYRGBA aaa)
{
}

long int f2(int b, const struct MYRGBA aaa)
{
  return int();
}

long int f3(float b, const struct MYRGBA aaa,  char c)
{
  return int();
}

На все эти функции анализатор выдаст три сообщения V801: Decreased performance. It is better to redefine the N function argument as a reference. Сообщение в подобном коде будет ложным, так как компилятор сам оптимизирует код, и проблемы не будет.

Можно, конечно, каждое сообщение пометить как False Alarm с помощью функции Mark As False Alarm. Однако, есть способ лучше. Достаточно добавить в код строку:

//-V:MYRGBA:801 

Для C/C++ проектов, мы рекомендуем добавлять такую строку в .h-файл рядом с объявлением структуры, но если это невозможно (например, структура в системном .h-файле), то можно прописать это в stdafx.h.

И тогда, после перепроверки, все три сообщения V801 будут автоматически помечены как False Alarm.

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

Рассмотрим несколько примеров:

//-V:<<:128

Подавит предупреждения V128 в строках, где имеется оператор <<.

buf << my_vector.size();

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

//-V:log<<:128
buf << my_vector.size(); // Есть предупреждение
log << my_vector.size(); // Нет предупреждения

Примечание. Обратите внимание, что строка для поиска не должна содержать пробелов.

Правильно: //-V:log<<:128
Неправильно: //-V:log <<:128

При поиске подстроки пробелы игнорируются. Но не беспокойтесь, следующая ситуация обработается корректно:

//-V:ABC:501
AB C = x == x; // Есть предупреждение
AB y = ABC == ABC; // Нет предупреждения

Полное отключение предупреждений

Существует возможность с помощью комментария полностью выключить предупреждение. В этом случае после двух двоеточий указывается номер отключаемой диагностики. Синтаксис:

//-V::(number) - для отключения одной диагностики

Для отключения нескольких диагностик можно перечислить их номера через запятую. Синтаксис:

//-V::(number1),(number2),...,(numberN) - для отключения сразу нескольких диагностик

Для отключения всех диагностик C++ или C# анализатора следует использовать следующую форму:

//-V::C++ или //-V::C#

Если требуется проигнорировать предупреждение V122, то можно указать в начале файла:

//-V::122

Если требуется, например, игнорировать предупреждения V502, V502 и V525, то в начале файла можно указать:

//-V::502,507,525

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

Другие способы фильтрации сообщений в анализаторе PVS-Studio (Detectable Errors, Don't Check Files, Keyword Message Filtering)

Возможны ситуации, в которых определённый тип диагностик не актуален для анализируемого проекта, или какая-либо из диагностик анализатора выдаёт предупреждения на код, в корректности которого вы уверены. В таком случае можно воспользоваться системой группового подавления сообщений, основанной на фильтрации полученных результатов анализа. Список доступных режимов фильтрации можно открыть через общее меню PVS-Studio -> Options.

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

Во-первых, можно отключить диагностику тех или иных ошибок по их коду. Это делается с помощью вкладки "Настройки: Detectable Errors". На вкладке обнаруживаемых ошибок можно указать номера ошибок, которые не надо показывать в отчете по анализу. Иногда бывает целесообразно убрать в отчете ошибки с определенными кодами. Например, если вы уверены, что ошибки, связанные с явным приведением типа (коды V201, V202, V203), вас не интересуют, то вы можете скрыть их показ. Также отображение ошибок определённого типа можно отключить с использованием команды контекстного меню "Hide all Vxxx errors". Соответственно, в случае, если необходимо включить отображение обратно, настроить это можно на упоминавшейся выше вкладке "Detectable Errors".

Во-вторых, можно отключить анализ некоторых частей проекта (некоторых папок или файлов проекта). Раздел "Настройки: Don't Check Files". На этой вкладке можно ввести информацию о библиотеках, включения (через директиву #include) из файлов которых анализировать не надо. Это может потребоваться для уменьшения количества лишних диагностических сообщений. Например, в проекте используется библиотека Boost. И хотя на какой-то код из этой библиотеки анализатор выдает диагностические сообщения, вы считаете, что эта библиотека является достаточно надежной и написана хорошо. Поэтому, возможно, не имеет смысла получать диагностические сообщения по поводу кода в этой библиотеке. В этом случае можно отключить анализ файлов из этой библиотеки, указав путь к ней на странице настроек. Кроме того, возможно ввести файловые маски для исключения некоторых файлов из анализа. Анализатор не будет проверять файлы, удовлетворяющие условиям маски. Например, подобным образом можно исключить из анализа автогенерируемые файлы.

Маски путей для файлов, сообщения из которых попали в текущий сгенерированный отчёт, можно автоматически добавить в список Don't Check Files с помощью команды контекстного меню "Don't check files and hide all messages from..." для выделенного в окне PVS-Studio Output сообщения (рисунок 4).

Рисунок 4 — Добавление масок путей через контекстное меню

Рисунок 4 — Добавление масок путей через контекстное меню

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

В-третьих, можно подавлять отдельные сообщения по тексту. На вкладке "Настройки: Keyword Message Filtering" можно настроить фильтрацию ошибок по содержащемуся в них тексту, а не по коду. При необходимости можно скрыть из отчета сообщения о диагностированных ошибках, содержащих определенные слова или фразы. Например, если в отчете есть ошибки, в которых указаны названия функций printf и scanf, а вы считаете, что ошибок, связанных с ними, быть не может, то просто добавьте эти два слова с помощью редактора подавляемых сообщений.

Массовое подавление сообщений анализатора (Mass Suppression)

Иногда, особенно на стадии внедрения статического анализа в крупных проектах, может возникнуть необходимость 'подавить' все предупреждения анализатора на имеющуюся кодовую базу, т.к. разработчики могут не иметь необходимых ресурсов для исправления найденных анализатором ошибок в старом коде. В таком случае может быть полезно 'скрыть' все предупреждения, выданные на имеющийся код, чтобы отслеживать только вновь появляющиеся ошибки. Этого можно достичь за счёт использования механизма "массового подавления сообщений анализатора". Использование соответствующего механизма в среде Windows описано в документе "Массовое подавление сообщений анализатора", в среде Linux – в соответствующем разделе документа "Как запустить PVS-Studio в Linux".

Возможные проблемы

В редких случаях автоматически расставленные разметки могут быть поставлены не в том месте, где должны быть. И тогда анализатор вновь выдаст эти же сообщения об ошибках, так как маркер не будет найден. Это проблема препроцессора, связанная с многострочными #pragma-директивами определенного типа, из-за которых также сбивается нумерация строк. Решением проблемы является пометка сообщений, на которых заметен сбой, вручную. PVS-Studio всегда сообщает о подобных ошибках сообщением "V002. Some diagnostic messages may contain incorrect line number".

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


А ты совершаешь ошибки в коде?

Проверь с помощью
PVS-Studio

Статический анализ
кода для C, C++ и C#

goto PVS-Studio;