>
>
Заметка о новых настройках диагностик

Андрей Карпов
Статей: 671

Заметка о новых настройках диагностик

Несмотря на то, что наш анализатор позиционирует себя как одно из наиболее простых средств в установке и повседневном использовании (всё работает "из коробки" и не требует никакой особенной настройки), некоторым пользователям иногда недостаёт гибкости отдельных диагностик. К сожалению, простота использования и гибкость настроек - требования если уж не противоположные по своему духу, то иногда несколько конфликтующие друг с другом. В данной заметке речь пойдёт о дополнительном механизме настройки некоторых диагностик, который может пригодиться некоторым из наших уважаемых пользователей.

Настройки

Ранее уже можно было управлять некоторыми диагностиками с помощью специальных комментариев. Про них можно узнать из описания диагностик в документации. Например, см. комментарий //-V610_LEFT_SIGN_OFF для диагностики V610.

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

Синтаксис новых настроек

Общий синтаксис нового механизма следующий: для того, чтобы активировать некоторое дополнительное правило, требуется использовать комментарий //+V. Мы выбрали такую форму из-за того, что она похожа на уже широко использующейся комментарий для подавления ложных срабатываний: //-V. Формат добавления нового правила для некоторой диагностики довольно прост:

//+V<Номер диагностики>, <Ключ>:<Значение>, ... , <Ключ>:<Значение>

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

Настройки диагностик могут быть записаны:

  • Во включаемом во весь проект файле (stdafx.h),
  • Если они касаются некоторой функции или метода класса - рядом с их прототипами или реализацией в заголовочном файле,
  • Если требуется, чтобы настройка действовала на один .cpp-файл - в любом месте этого .cpp-файла.

Ограничение - правило должно быть записано в одну строчку.

Примеры:

//+V128,function:N128_write_it,non_memsize:2,class:N128_T

//+V576,function:foo,format_arg:1,ellipsis_arg:3,namespace:X,class:Y

На момент написания статьи новые настройки реализованы для двух диагностик: V128 и V576. В дальнейшем, могут появиться новые настройки для других диагностик.

Настройка диагностики V128

Диагностика V128 предназначена для поиска в коде мест, где переменная memsize-типа записывается в файл или считывается из файла. Полное описание диагностики можно прочесть по ссылке, а здесь будет рассказано про её новую настройку. Один из пользователей попросил расширить диагностику V128 путём добавления возможности выдачи предупреждения на пользовательские функции.

Формат расширения следующий: возле прототипа функции (или возле её реализации, или в общем заголовочном файле) пишется комментарий специального вида. Начнём с примера использования:

//+V128, function:write, non_memsize:2
void write(string name, char);
void write(string name, int32);
void write(string name, int64);
foo()
{
  write("zz", array.size()); // warning V128
}

Формат:

  • Ключ function задаёт имя функции, на которую будет обращать внимание анализатор. Это обязательный ключ - без его указания расширение, разумеется, работать не будет.
  • Ключ class - необязательный ключ, который позволяет указать класс, к которому относится функция (то есть в этом случае - метод класса). Без указания этого ключа анализатор будет обращать внимание на все функции с данным именем, с указанием - лишь на методы указанного класса.
  • Ключ namespace - необязательный ключ, который позволяет указать пространство имён, к которому относится функция. Без указания ключа, опять же, анализатор будет обращать внимание на все функции, с указанием - лишь на функции, принадлежащие к заданному параметром пространству имён. Ключ может быть задан совместно с ключом class - тогда анализатор будет обращать внимание лишь на методы некоторого класса из некоторого пространства имён.
  • Ключ non_memsize позволяет задавать номер аргумента, в который нельзя погружать аргумент меняющегося в зависимости от архитектуры размера. Номера считаются с единицы. Также имеется техническое ограничение - этот номер не должен превышать число 14. Ключей non_memsize может быть задано несколько, если требуется обращать внимание сразу на несколько аргументов.

Уровень диагностики в случае срабатывания на пользовательской функции - всегда первый.

Напоследок дадим наиболее полный пример использования:

//Предупреждать, когда в метод C класса B
//из пространства имён A во второй или третий
//аргумент была помещена переменная типа memsize
//+V128,namespace:A,class:B,function:C,non_memsize:3,non_memsize:2

Настройка диагностики V576

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

Опять же, сказано - сделано.

Формат расширения аналогичен формату настройки диагностики V128. Возле прототипа функции (или возле её реализации, или в общем заголовочном файле) пишется комментарий специального вида. Начнём, опять же, с примера использования:

//+V576, function:Mylog, format_arg:1, ellipsis_arg:2
Mylog("%f", time(NULL)); // warning V576

Формат:

  • Ключи function, class и namespace задают имя функции, имя класса (если нужно анализировать вызов только этого метода класса) и имя пространства имён (если требуется анализировать функцию или метод класса только этого пространства имён).
  • Ключ format_arg задаёт номер аргумента функции, в котором будет находиться форматная строка. Это обязательный аргумент. Номера также считаются с единицы и также не должны превышать число 14.
  • Ключ ellipsis_arg задаёт номер аргумента функции с эллипсисом (то есть многоточием). К этому номеру предъявляются те же ограничения, что и к номеру форматной строки. Более того, номер аргумента с эллипсисом должен быть больше номера аргумента с форматной строкой (всё-таки эллипсис - исключительно последний аргумент). Это также обязательный аргумент.

Напоследок, дадим наиболее полный пример использования:

//Предупреждать, когда в методе C класса B
//из пространства имён A аргументы, начиная с
//третьего, не совпадают с типом, заданным в
//форматной строке второго аргумента. 
//+V576,namespace:A,class:B,function:C,format_arg:2,ellipsis_arg:3

Заключение

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