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

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

## Настройки

Ранее уже можно было управлять некоторыми диагностиками с помощью специальных комментариев\. Про них можно узнать из описания диагностик в документации\. Например, см\. комментарий //\-V610\_LEFT\_SIGN\_OFF для [диагностики V610](https://pvs-studio.ru/ru/docs/warnings/v610/)\.

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

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

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

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

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

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

* Во включаемом во весь проект файле \([stdafx\.h](https://pvs-studio.ru/ru/blog/posts/cpp/0265/)\),
* Если они касаются некоторой функции или метода класса \- рядом с их прототипами или реализацией в заголовочном файле,
* Если требуется, чтобы настройка действовала на один \.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](https://pvs-studio.ru/ru/docs/warnings/v128/) предназначена для поиска в коде мест, где переменная [memsize\-типа](https://pvs-studio.ru/ru/blog/terms/0030/) записывается в файл или считывается из файла\. Полное описание диагностики можно прочесть по ссылке, а здесь будет рассказано про её новую настройку\. Один из пользователей попросил расширить диагностику V128 путём добавления возможности выдачи предупреждения на пользовательские функции\.

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

```cpp
//+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 может быть задано несколько, если требуется обращать внимание сразу на несколько аргументов\.

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

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

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

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

[Диагностика V576](https://pvs-studio.ru/ru/docs/warnings/v576/) предназначена для поиска несоответствия аргументов функций наподобие printf\(\) их форматной строке\. Потенциальный пользователей попросил расширить эту диагностику на случай собственных функций\. Нужно, чтобы анализатор выдавал предупреждения на случай, если в некотором месте в функцию помещён аргумент, несоответствующий форматной строке\. Подразумевается, что принцип форматирования строк совпадает с функцией printf\(\)\.

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

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

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

Формат:

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

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

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

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

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