﻿# Аннотирование C и C\+\+ кода в формате JSON

Механизм пользовательских аннотаций — это способ разметки типов и функций в формате JSON с целью дать анализатору дополнительную информацию\. Благодаря этой информации анализатор сможет находить больше ошибок в коде\. Механизм работает только для языков С и С\+\+\.

## Быстрый старт

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

```cpp
void DeprecatedFunction(); // should not be used

void foo()
{
  DeprecatedFunction();    // unwanted call site
}
```

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

```cpp
{
  "version": 5,
  "annotations": [
    {
      "type": "function",
      "name": "DeprecatedFunction",
      "attributes": [ "dangerous" ]
    }
  ]
}
```

После этого достаточно подключить файл \(все способы подключения рассмотрены [здесь](https://pvs-studio.ru/ru/docs/manual/6810/#how_to_enable)\): 

```cpp
//V_PVS_ANNOTATIONS, language: cpp, path: %path/to/annotations.json%

void DeprecatedFunction();

void foo()
{
  DeprecatedFunction();    // <= V2016 will be issued here
}
```

**Примечание\. **По умолчанию диагностическое правило V2016 отключена\. Для выдачи предупреждений включите диагностику в [настройках](https://pvs-studio.ru/ru/docs/manual/0013/)\.

## Способы подключения файла c аннотациями

Подробнее о способе подключения файла аннотаций вы можете узнать в [этой](https://pvs-studio.ru/ru/docs/manual/6810/#how_to_enable) документации\.

## Структура файла с аннотациями

Содержимое файла — JSON\-объект, состоящий из двух обязательных полей: `version` и `annotations`\. 

Поле `version` принимает значение целого типа и задаёт версию механизма\. В зависимости от значения файл с разметкой может обрабатываться по\-разному\. На данный момент поддерживается только одно значение — `1`\.

Поле `annotations` — массив объектов "аннотация":

```cpp
{
  "version": 5,
  "annotations":
  [
    {
      ...
    },
    {
      ...
    }
  ]
}
```

Аннотации могут быть двух видов:

* аннотации типов;
* аннотации функций\.

Если аннотация объявляется непосредственно в массиве `annotations`, то она считается **аннотацией верхнего уровня**\. В ином случае считается, что это **вложенная аннотация**\.

## Аннотации типов

Объект аннотации типа состоит из следующих полей:

### Поле "type"

Обязательное поле\. Принимает строку с одним из значений: `"record"`, `"class"`, `"struct"`, `"union"`\. Последние три варианта являются псевдонимами "record" и добавлены для удобства\.

### Поле "name"

Обязательное поле\. Принимает строку с квалифицированным \(полным\) именем сущности\. Анализатор будет искать эту сущность, начиная с глобальной области видимости\. Если сущность находится в глобальной области, то `"::"` в начале имени может быть опущено\.

### Поле "members"

Опциональное поле\. Массив объектов вложенных аннотаций\. 

### Поле "attributes"

Опциональное поле\. Массив строк, который задаёт свойства сущности\. Для аннотаций типов доступны следующие атрибуты:

#### Умные указатели

* `"unique_ptr"` — тип имеет интерфейс [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr);
* `"shared_ptr"` — тип имеет интерфейс [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr);
* `"auto_ptr"` — тип имеет интерфейс [`std::auto_ptr`](https://en.cppreference.com/w/cpp/memory/auto_ptr);

#### Контейнеры

* `"string"` — тип имеет интерфейс [`std::basic_string`](https://en.cppreference.com/w/cpp/string/basic_string);
* `"string_view"` — тип имеет интерфейс [`std::basic_string_view`](https://en.cppreference.com/w/cpp/string/basic_string_view);
* `"array"` — тип имеет интерфейс [`std::array`](https://en.cppreference.com/w/cpp/container/array);
* `"vector"` — тип имеет интерфейс [`std::vector`](https://en.cppreference.com/w/cpp/container/vector);
* `"map"` — тип имеет интерфейс [`std::map`](https://en.cppreference.com/w/cpp/container/map);
* `"set"` — тип имеет интерфейс [`std::set`](https://en.cppreference.com/w/cpp/container/set);
* `"list"` — тип имеет интерфейс [`std::list`](https://en.cppreference.com/w/cpp/container/list);
* `"iterator"` — тип имеет интерфейс [`std::iterator`](https://en.cppreference.com/w/cpp/iterator/iterator.html);
* `"unordered"` — в сочетании с "set" или "map" задаёт типу интерфейс [`std::unordered_set`](https://en.cppreference.com/w/cpp/container/unordered_set) или [`std::unordered_map`](https://en.cppreference.com/w/cpp/container/unordered_map) соответственно\. 
* `"multi"` — в сочетании с `"map"` или `"set"` задаёт типу интерфейс [`std::multiset`](https://en.cppreference.com/w/cpp/container/multiset) или [`std::multimap`](https://en.cppreference.com/w/cpp/container/multimap)\. Если также присутствует "unordered", то типу задаётся семантика [`std::unordered_multiset`](https://en.cppreference.com/w/cpp/container/unordered_multiset) или [`std::unordered_multimap`](https://en.cppreference.com/w/cpp/container/unordered_multimap)\.



#### Другие типы

* `"nullable"` — тип имеет семантику `nullable`\-типа\. Объекты таких типов могут находиться в одном из двух состояний: "валидный" или "невалидный"\. Доступ к объекту в состоянии "невалидный" приведёт к ошибке\. Примерами таких типов являются указатели, итераторы и [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional)\.
* `"exception"` — тип используется для создания объекта, который будет выброшен как исключение\. Пример использования можно посмотреть [здесь](https://pvs-studio.ru/ru/docs/manual/6743/#exception_annotation)\.



#### Семантика

* `"cheap_to_copy"` — объект типа может передаваться в функцию по копии без накладных расходов;
* `"expensive_to_copy"` — объект типа следует передавать в функцию только по указателю/ссылке;
* `"copy_on_write"` — тип имеет семантику [copy\-on\-write](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8)\.



## Аннотации функций

Объект аннотации функции состоит из следующих полей:

### Поле "type"

Обязательное поле\. Принимает строку со значением `function`\. Для вложенных аннотаций функций \(в поле `members`_ _аннотаций типов\) также становится доступным значение `ctor`\. Оно показывает, что аннотируется конструктор пользовательского типа\.

### Поле "name"

Принимает строку с именем функции\. Поле обязательно, если `type` имеет значение `"function"`, иначе должно быть опущено\. По этому имени анализатор будет искать аннотируемую сущность, начиная с глобальной области видимости\.

Для аннотаций верхнего уровня указывается квалифицированное \(полное\) имя, для вложенных — неквалифицированное\.

Если функция находится в глобальной области видимости, то `'::'` в начале имени может быть опущено\.

### Поле "params"

Опциональное поле\. Массив объектов, описывающих формальные параметры\. Данное поле совместно с `name` задаёт сигнатуру функции, по которой анализатор будет сравнивать аннотацию с её объявлением в коде программы\. В случае с функциями\-членами анализатор также рассматривает поле `qualifiers`\.

Каждый объект содержит следующие поля:

* `"type"` \(обязательное\) — тип формального параметра в виде строки\. Например, первый формальный параметр функции _memset_ имеет тип _void \*_\. Его и следует записать в строку\. Существует возможность пропустить не интересующие параметры и проаннотировать несколько перегрузок функции с помощью одной аннотации: для этого в типе можно написать символ подстановки:
  * Символ `*` означает, что на его месте может быть 0 или более параметров любого типа\. Должен идти последним в списке параметров\.
  * Символ `?` означает, что на его месте может быть параметр любого типа\. 
* `"attributes"` \(опциональное\) — массив строк, который задаёт свойства параметра\. Возможные атрибуты параметров описаны далее в документации\.
* `"constraint"` \(опциональное\) — объект, содержащий информацию об ограничениях параметра\. Если анализатор находит возможное нарушение ограничений, то пользователю будет выдано предупреждение [V1108](https://pvs-studio.ru/ru/docs/warnings/v1108/)\. Возможные поля объекта описаны далее в документации\.

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

```cpp
// Code
void foo();      // dangerous
void foo(int);   // dangerous
void foo(float); // dangerous

// Annotation
{
  ....
  "type": "function",
  "name": "foo",
  "attributes": [ "dangerous" ]
  ....
}
```

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

```cpp
// Code
void foo();      // dangerous
void foo(int);   // ok
void foo(float); // ok

// Annotation
{
  ....
  "type": "function",
  "name": "foo",
  "attributes": [ "dangerous" ],
  "params": []
  ....
}
```

#### Возможные значения атрибутов параметров

|\\\#|Название атрибута|Описание атрибута|
|---|---|---|
|1|\`immutable\`|Подсказывает анализатору, что после вызова функции переданный аргумент не был модифицирован\\\. Например, функция \[\`printf\`\]\(https://en\.cppreference\.com/w/cpp/io/c/fprintf\) имеет побочные эффекты \\\(печать в \`stdout\`\\\), однако не производит модификации переданных аргументов\\\.|
|2|\`not\_null\`|Действует только для параметров \`nullable\`\\\-типа\\\. В функцию необходимо передавать аргумент в состоянии "валидный"\\\.|
|3|\`unique\_arg\`|Передаваемые аргументы должны отличаться\\\. Например, нет смысла передавать в \[\`std::swap\`\]\(https://en\.cppreference\.com/w/cpp/algorithm/swap\) два одинаковых аргумента\\\.|
|4|\`format\_arg\`|Параметр обозначает форматную строку\\\. Анализатор будет проверять аргументы согласно форматной спецификации \[\`printf\`\]\(https://en\.cppreference\.com/w/cpp/io/c/fprintf\)\\\.|
|5|\`pointer\_to\_free\`|Указатель, по которому в функции будет освобождена память с помощью \[\`free\`\]\(https://en\.cppreference\.com/w/c/memory/free\)\\\. Указатель может быть нулевым\\\.|
|6|\`pointer\_to\_gfree\`|Указатель, по которому в функции будет освобождена память с помощью \[\`g\_free\`\]\(https://docs\.gtk\.org/glib/func\.free\.html\)\\\. Указатель может быть нулевым\\\.|
|7|\`pointer\_to\_delete\`|Указатель, по которому в функции будет освобождена память с помощью \`operator delete\`\\\. Указатель может быть нулевым\\\.|
|8|\`pointer\_to\_delete\[\]\`|Указатель, по которому в функции будет освобождена память с помощью \`operator delete\[\]\`\\\. Указатель может быть нулевым\\\.|
|9|\`pointer\_to\_unmap\`|Указатель, по которому в функции будет освобождена память с помощью \[\`munmap\`\]\(https://man7\.org/linux/man\-pages/man3/munmap\.3p\.html\)\\\. Указатель может быть нулевым\\\.|
|10|\`taint\_source\`|Данные, возвращающиеся через параметр, получены из недостоверного источника\\\.|
|11|\`taint\_sink\`|Данные, передаваемые через параметр, могут привести к эксплуатации уязвимости, если они получены из недостоверного источника\\\.|
|12|\`non\_empty\_string\`|Параметр должен принимать любую непустую строку\\\.|
|13 |\`lower\_bound\`|Параметр обозначает нижнюю границу некоторого диапазона\\\. Например, второй параметр \[\`std::clamp\`\]\(https://en\.cppreference\.com/w/cpp/algorithm/clamp\)\\\.|
|14|\`upper\_bound\`|Параметр обозначает верхнюю границу некоторого диапазона\\\. Например, третий параметр \[\`std::clamp\`\]\(https://en\.cppreference\.com/w/cpp/algorithm/clamp\)\\\.|
|15|\`in\_range\_value\`|Значение, переданное в параметр, проверяется на вхождение в диапазон\\\. Например, первый параметр \[\`std::clamp\`\]\(https://en\.cppreference\.com/w/cpp/algorithm/clamp\)\\\. Диапазон задаётся параметрами, проаннотированными как \`lower\_bound\` и \`upper\_bound\`\\\.|

#### Возможные поля ограничений параметров

Все поля ограничений — опциональные\. Далее приведён список полей, которые задают условия ограничения\.

Поля, задающие список разрешённых и запрещённых значений параметра:

* Поле `allowed` — массив строк\. Задаёт список разрешённых интегральных значений, которые может принимать параметр функции\. По умолчанию значения, не перечисленные в этом списке, запрещены\.
* Поле `disallowed` — массив строк\. Задаёт список запрещённых интегральных значений, которые может принимать параметр функции\. По умолчанию значения, не перечисленные в этом списке, разрешены\.

Каждая строка внутри массива — это интервал от минимальной до максимальной границы **включительно**\. Строка с интервалами указывается в формате `"x..y"`, где `x` и `y` — это левая и правая границы соответственно\. Одну из границ можно опустить\. Тогда строка будет иметь вид `"x.."` или `"..y"`\. В таком случае интервал будет от `x` до плюс бесконечности и от минус бесконечности до `y` соответственно\.

Примеры интервалов:

* `"0..10"` — строка, задающая интервал от 0 до 10 включительно\.
* `"..10"` — строка, задающая интервал от минус бесконечности до 10 включительно\.
* `"0.."` — строка, задающая интервал от 0 до плюс бесконечности\.

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

Если поля `allowed` и `disallowed` указаны одновременно, то анализатор вычитает `"disallowed"` интервалы из `"allowed"`, чтобы получить множество разрешённых значений\. Если значения из поля `disallowed` полностью перекрывают значения из `allowed`, то пользователю будет выдано предупреждение [V019](https://pvs-studio.ru/ru/docs/warnings/v019/)\.

### Поле "returns"

Опциональное поле\. Объект, внутри которого возможно использование только поля `attributes` — массива строк, который позволяет задать атрибуты возвращаемого значения\.

#### Возможные значения атрибутов возвращаемого результата

|\\\#|Название атрибута|Описание атрибута|
|---|---|---|
|1|\`not\_null\`|Функция всегда возвращает объект \`nullable\`\\\-типа в состоянии "валидный"\\\.|
|2|\`maybe\_null\`|Функция может вернуть объект \`nullable\`\\\-типа в состоянии "невалидный", и его стоит проверить перед разыменованием\\\.|
|3|\`taint\_source\`|Функция может вернуть данные из недостоверного источника\\\.|
|4|\`always\_null\`|Функция всегда возвращает объект \`nullable\`\\\-типа в состоянии "невалидный"\\\.|

### Поле "template\_params"

Опциональное поле\. Массив строк, позволяющий задать шаблонные параметры функции\. Поле необходимо, когда шаблонные параметры используются в сигнатуре функции:

```cpp
// Code
template <typename T1, class T2>
void MySwap(T1 &lhs, T2 &rhs);

// Annotation
{
  ....
  "template_params": [ "typename T1", "class T2" ],
  "name": "MySwap",
  "params": [
    { "type": "T1 &", attributes: [ "unique_arg" ] },
    { "type": "T2 &", attributes: [ "unique_arg" ] }
  ]
  ....
}
```

### Поле "qualifiers"

Опциональное поле\. Позволяет применить аннотацию только к функции\-члену с определённым набором `cvref`\-квалификаторов\. Доступно только для вложенных аннотаций, у которых поле `type` имеет значение `"function"`\. Данное поле совместно с _name_ и _params_ задаёт сигнатуру нестатической функции\-члена, по которой анализатор будет сравнивать аннотацию с её объявлением в коде программы\. Принимает массив строк со следующими возможными значениями: `"const"`, `"volatile"`, `"&"` или `"&&"`\.

**Пример:**

```cpp
// Code
struct Foo
{
  void Bar();                // don't need to annotate this overload
  void Bar() const;          // want to annotate this overload
  void Bar() const volatile; // and this one
};

// Annotation
{
  ....
  "type": "record",
  "name": "Foo",
  "members": [
    {
      "type": "function",
      "name": "Bar",
      "qualifiers": [ "const" ]
    },
    {
      "type": "function",
      "name": "Bar",
      "qualifiers": [ "const", "volatile" ]
    }
  ]
  ....
}
```

Если аннотацию надо применить ко всем квалифицированным и неквалифицированным версиям, то нужно опустить поле:

```cpp
// Code
struct Foo
{
  void Bar();       // want to annotate this overload
  void Bar() const; // and this one
};

// Annotation
{
  ....
  "type": "record",
  "name": "Foo",
  "members": [
    {
      "type": "function",
      "name": "Bar",
    }
  ]
  ....
}
```

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

```cpp
// Code
struct Foo
{
  void Bar();       // want to annotate this overload
  void Bar() const; // but NOT this one
};

// Annotation
{
  ....
  "type": "record",
  "name": "Foo",
  "members": [
    {
      "type": "function",
      "name": "Bar",
      "qualifiers": []
    }
  ]
  ....
}
```

### Поле "attributes"

Опциональное поле\. Массив строк, который задаёт свойства сущности\.

#### Возможные атрибуты функций и конструкторов 

|\\\#|Название атрибута|Описание атрибута|Примечание|
|---|---|---|---|
|1|\`pure\`|Функция считается \[чистой\]\(https://ru\.wikipedia\.org/wiki/%D0%A7%D0%B8%D1%81%D1%82%D0%BE%D1%82%D0%B0\_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8\)\\\.|Функция чистая, когда она не имеет побочных эффектов, не модифицирует переданные аргументы, и результат функции одинаков при исполнении на одном и том же наборе аргументов\\\.|
|2|\`noreturn\`|Функция не возвращает управление вызывающей функции\\\.||
|3|\`nodiscard\`|Результат функции должен использоваться\\\.||
|4|\`nullable\_uninitialized\`|Функция\\\-член пользовательского \`nullable\`\\\-типа переводит объект в состоянии "невалидный"\\\.||
|5|\`nullable\_initialized\`|Функция\\\-член пользовательского \`nullable\`\\\-типа переводит объект в состоянии "валидный"\\\.||
|6|\`nullable\_potentially\_initialized\`|Функция\\\-член пользовательского \`nullable\`\\\-типа переводит объект в состоянии "потенциально валидный"\\\.||
|7|\`nullable\_checker\`|Функция проверяет состояние пользовательского \`nullable\`\\\-типа\\\. Если функция возвращает \`true\`, то объект считается в состоянии "валидный", не возвращает — "невалидный"\\\. Результат функции должен неявно конвертироваться к типу \`bool\`\\\.||
|8|\`nullable\_getter\`|Функция производит доступ к внутренним данным пользовательского \`nullable\`\\\-типа\\\. Объект при этом должен быть в состоянии "валидный"\\\.||
|9|\`nullable\_resetter\`|Функция пользовательского \`nullable\`\\\-типа изменяет состояние объекта\\\. Если функция не принимает параметров, объект переводится в состояние "невалидный"\\\. Если функция принимает один или более параметров, новое состояние и значение объекта определяются на основе переданных аргументов\\\.||
|10|\`dangerous\`|Функция помечена как опасная, и код программы не должен содержать её вызова\\\.|Можно использовать в качестве пометки функции как устаревшей \\\(deprecated\\\)\\\.<br>Для выдачи предупреждений требуется включить диагностическое правило \[V2016\]\(https://pvs\-studio\.ru/ru/docs/warnings/v2016/\) в \[настройках\]\(https://pvs\-studio\.ru/ru/docs/manual/0013/\)\\\.|
|11|\`memory\_allocator\`|Функция, которая выделяет память\\\.||
|12|\`alloc\-group \(\.\.\.\.\)\`|Атрибут для указания пары аллокаторов/деаллокаторов\\\.|Внутри скобок указывается один или несколько тегов через запятую\\\. Память, выделенная аллокатором из группы N, может быть освобождена любым деаллокатором из этой же группы\\\.<br>Для совместимости функции со стандартной библиотекой используется имя группы \`stdlib\`\\\.|
|13|\`constructor\_like\`|Функция, инициализирующая поля класса и трактуемая анализатором как конструктор \\\(инициализатор\\\)\\\.||

Ниже приведена таблица применимости различных атрибутов с аннотациями функций:

|\\\#|Атрибут|Свободная функция|Конструктор|Функция\\\-член|
|---|---|---|---|---|
|1|pure|✓|✕|✓|
|2|noreturn|✓|✕|✓|
|3|nodiscard|✓|✓|✓|
|4|nullable\\\_uninitialized|✕|✓|✓|
|5|nullable\\\_initialized|✕|✓|✓|
|6|nullable\\\_potentially\\\_initialized|✕|✓|✓|
|7|nullable\\\_checker|✕|✕|✓|
|8|nullable\\\_getter|✕|✕|✓|
|9|nullable\\\_resetter|✕|✕|✓|
|10|dangerous|✓|✕|✓|
|11|memory\\\_allocator|✓|✕|✓|
|12|alloc\\\-group \\\(\\\.\\\.\\\.\\\.\\\)|✓|✕|✓|
|13|constructor\\\_like|✓|✕|✓|

## Схема JSON

JSON схемы поставляется в дистрибутиве, а также доступны по ссылкам ниже:

* [V1 \(поддерживается начиная с версии 7\.31\)](https://files.pvs-studio.ru/media/custom_annotations/v1/cpp-annotations.schema.json);
* [V2 \(поддерживается начиная с версии 7\.33\)](https://files.pvs-studio.ru/media/custom_annotations/v2/cpp-annotations.schema.json);
* [V3 \(поддерживается начиная с версии 7\.39\)](https://files.pvs-studio.ru/media/custom_annotations/v3/cpp-annotations.schema.json);
* [V4 \(поддерживается начиная с версии 7\.43\)](https://files.pvs-studio.ru/media/custom_annotations/v4/cpp-annotations.schema.json);
* [V5 \(поддерживается начиная с версии 8\.0\)](https://files.pvs-studio.ru/media/custom_annotations/v5/cpp-annotations.schema.json)\.

## Примеры

### Как проаннотировать свой nullable тип

Допустим, есть следующий пользовательский `nullable`\-тип:

```cpp
constexpr struct MyNullopt { /* .... */ } my_nullopt;

template <typename T>
class MyOptional
{
public:
  MyOptional();
  MyOptional(MyNullopt);

  MyOptional(T &&val);

  template <typename U>
  MyOptional(U &&val);

public:
  bool HasValue() const;

  T& Value();
  const T& Value() const;

  void SetValue(T val);

  void Reset();
  void Reset(T &&val);

private:
  /* implementation */
};
```

Примечания по коду:

* Конструктор по умолчанию и конструктор от типа `MyNullopt` инициализируют объект в состоянии "невалидный"\.
* Конструктор, принимающий параметр типа `T&&`, инициализирует объект в состоянии "потенциально валидный"\.
* Шаблон конструктора, принимающий параметр типа `U&&`, инициализирует объект в состоянии "валидный"\.
* Функция\-член `HasValue` проверяет состояние объекта `nullable`\-типа\. Если объект в состоянии "валидный", то возвращается `true`, в обратном случае — `false`\. Функция не меняет состояния объекта `nullable`\-типа\.
* Перегрузки функций\-членов `Value` возвращают нижележащий объект\. Функции не меняют состояние объекта `nullable`\-типа\.
* Функция\-член `SetValue` изменяет состояние нижележащего объекта и переводит объект в состояние "потенциально валидный"\.
* Перегрузки функций\-членов `Reset` изменяют состояние нижележащего объекта\. Перегрузка без параметров переводит объект в состояние "невалидный"\. Перегрузка с параметрами переводит объект в состояние, вычисленное на основе значений переданных аргументов\.

Тогда аннотация класса и его функций\-членов будет выглядеть следующим образом:

```cpp
{
  "version": 5,
  "language": "cpp",
  "annotations": [
    {
      "type": "class",
      "name": "MyOptional",
      "attributes": [ "nullable" ],
      "members": [
        {
          "type": "ctor",
          "attributes": [ "nullable_uninitialized" ]
        },
        {
          "type": "ctor",
          "attributes": [ "nullable_uninitialized" ],
          "params": [
            {
              "type": "MyNullopt"
            }
          ]
        },
        {
          "type": "ctor",
          "attributes": [ "nullable_potentially_initialized" ],
          "params": [
            {
              "type": "T &&val"
            }
          ]
        },
        {
          "type": "ctor",
          "template_params": [ "typename U" ],
          "attributes": [ "nullable_initialized" ],
          "params": [
            {
              "type": "U &&val"
            }
          ]
        },
        {
          "type": "function",
          "name": "HasValue",
          "attributes": [ "nullable_checker", "pure", "nodiscard" ]
        },
        {
          "type": "function",
          "name": "Value",
          "attributes": [ "nullable_getter", "nodiscard" ]
        },
        {
          "type": "function",
          "name": "SetValue",
          "attributes": [ "nullable_potentially_initialized" ]
        },
        {
          "type": "function",
          "name": "Reset",
          "attributes": [ "nullable_resetter" ]
        }
      ]
    }
  ]
}
```

### Как добавить контракт "всегда валидный" на параметр функции nullable\-типа

Допустим, есть следующий код:

```cpp
namespace Foo
{
  template <typaname CharT>
  size_t my_strlen(const CharT *ptr);
}
```

Функция `Foo::my_strlen` имеет следующие свойства:

* Первый параметр всегда должен быть ненулевым, т\.е\. в состоянии "валидный"\.
* Функция чистая и ничего не модифицирует\.

Тогда аннотация функции будет выглядеть следующим образом:

```cpp
{
  "version": 5,
  "annotations":
  [
    {
      "type": "function",
      "name": "Foo::my_strlen",
      "attributes": [ "pure" ],
      "template_params": [ "typename CharT" ],
      "params": [
        {
          "type": "const CharT *",
          "attributes": [ "not_null" ]
        }
      ]
    }
  ]
}
```

### Как разметить пользовательскую функцию форматного ввода/вывода

Допустим, есть следующая функция `Foo::LogAtError`:

```cpp
namespace Foo
{
  void LogAtError(const char *, ...);
}
```

О ней известны следующие факты:

* Первым параметром она принимает форматную строку согласно спецификации `printf`\. Аргумент не должен быть нулевым\.
* Соответствующие форматной строке аргументы передаются, начиная со второго\.
* Функция не модифицирует переданные аргументы\.
* Функция не возвращает управление после вызова\.

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

```cpp
{
  "version": 5,
  "annotations": [
    {
      "type": "function",
      "name": "Foo::LogAtError",
      "attributes": [ "noreturn" ],
      "params": [
        {
          "type": "const char *",
          "attributes" : [ "format_arg", "not_null", "immutable" ]
        },
        {
          "type": "...",
          "attributes": [ "immutable" ]
        }
      ]
    }
  ]
}
```

### Как пользоваться символом подстановки для аннотирования нескольких перегрузок

Допустим, что в предыдущем примере программист добавил несколько перегрузок функции `Foo::LogAtExit`:

```cpp
namespace Foo
{
  void LogAtExit(const     char *fmt, ...);
  void LogAtExit(const  char8_t *fmt, ...);
  void LogAtExit(const  wchar_t *fmt, ...);
  void LogAtExit(const char16_t *fmt, ...);
  void LogAtExit(const char32_t *fmt, ...);
}
```

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

```cpp
{
  "version": 5,
  "annotations": [
    {
      "type": "function",
      "name": "Foo::LogAtExit",
      "attributes": [ "noreturn" ],
      "params": [
        {
          "type": "?",
          "attributes" : [ "format_arg", "not_null", "immutable" ]
        },
        {
          "type": "...",
          "attributes": [ "immutable" ]
        }
      ]
    }
  ]
}
```

### Как пометить функцию как опасную для использования \(или устаревшую\)

Допустим, есть две перегрузки функции `Foo::Bar`:

```cpp
namespace Foo
{
  void Bar(int i);
  void Bar(double d);
}
```

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

```cpp
{
  "version": 5,
  "annotations": [
    {
      "type": "function",
      "name": "Foo::Bar",
      "attributes": [ "dangerous" ],
      "params": [
        {
          "type": "int"
        }
      ]
    }
  ]
}
```

### Как пометить функцию как источник/приёмник недостоверных данных

Допустим, что есть функция, которая возвращает внешние данные через `out`\-параметр и возвращаемое значение\.

```cpp
std::string ReadStrFromStream(std::istream &input, std::string &str)
{
  ....
  input >> str; 
  return str;
  ....
}
```

Для того, чтобы пометить эту функцию как источник недостоверных данных, необходимо её разметить следующим образом:

```cpp
{
  "version": 5,
  "annotations": [
    {
      "type": "function",
      "name": "ReadStrFromStream",
      "params": [
        {
          "type": "std::istream &input"
        },
        {
          "type": "std::string &str",
          "attributes": [ "taint_source" ]
        }
      ],
      "returns": { "attributes": [ "taint_source" ] }
    }
  ]
}
```

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

```cpp
void DoSomethingWithData(std::string &str)
{
  .... // Some vulnerability
}
```

Чтобы пометить такую функцию как приемник недостоверных данных \(функцию\-сток\), необходимо написать следующую аннотацию:

```cpp
{
  "version": 5,
  "annotations": [
    {
      {
      "type": "function",
      "name": "DoSomethingWithData",
      "params": [ { "type": "std::string &str",
                    "attributes": [ "taint_sink" ] }]
      }
    }
  ]
}
```

### Как проаннотировать пользовательское исключение, которое не должно создаваться без поясняющего сообщения

В C\+\+ анализаторе есть диагностическое правило [V1116](https://pvs-studio.ru/ru/docs/warnings/v1116/), которое срабатывает при обнаружении создания объекта исключения с пустым сообщением\. По умолчанию данное правило работает только с типами исключений из стандартной библиотеки C\+\+\. Для поддержки пользовательских типов исключений необходимо добавить специальную аннотацию\.

Предположим, есть класс, определяющий объекты, которые будут использованы для выброса исключения:



```cpp
class MyException: public std::runtime_error
{
public:
  MyException(const std::string& what_arg )
  : std::runtime_error(what_arg){}

   MyException(const char *what_arg)
   : std::runtime_error(what_arg){}
};
```

В аннотации необходимо определить тип исключения с атрибутом `exception` и затем конструкторы, способные принимать поясняющие сообщения\. Для каждого параметра конструктора, который должен принимать сообщение, следует указать атрибут `non_empty_string`\.

Например, аннотация класса `MyException` может выглядеть так:

```cpp
{
  "version": 5,
  "language": "cpp",
  "annotations": [
   {
    "type": "class",
    "name": "MyException",
    "attributes": ["exception"],
    "members": [
     {
     "type": "ctor",
     "params": [{ "type": "const char*",
                    "attributes": ["non_empty_string"]}]
     },
     {
     "type": "ctor",
     "params": [{ "type": "const std::string&", 
                  "attributes": ["non_empty_string"]}]
     }
    ]
   }
  ]
}
```

### Как разметить пользовательские контейнеры и их итераторы

В C\+\+ анализаторе есть диагностическое правило [V783](https://pvs-studio.ru/ru/docs/warnings/v783/), которое выдаёт предупреждение при обнаружении использования невалидного \(либо потенциально невалидного\) итератора\. По умолчанию данное правило работает только с контейнерами из стандартной библиотеки С\+\+\. 

Предположим, есть класс `MyIterator`, реализующий поведение итератора, и класс\-контейнер `MyContainer` с методами, которые его возвращают:

```cpp
class MyIterator{....};
class MyContainer
{
public:
  typedef MyIterator Iterator;
  std::list<MyIterator> vec;
  Iterator Begin();
  Iterator Find(int i);
  Iterator Find(Iterator it);
  Iterator End();
....
};
```

Для поддержки данной реализации необходимо проаннотировать тип `MyIterator` атрибутом `iterator`, а также разметить методы `Begin`, `Find` и `End` класса `MyContainer` соответствующими `returns` атрибутами\. Например:

```cpp
{
  "version": 5,
  "language": "cpp",
  "annotations": [
    {
      "type": "class",
      "name": "MyIterator",
      "attributes": [ "iterator" ]
    },
    {
      "type": "class",
      "name": "MyContainer",
      "members": [
        {
          "type": "function",
          "name": "Begin",
          "returns": { "attributes": [ "maybe_null" ] }
        },
        {
          "type": "function",
          "name": "End",
          "returns": { "attributes": [ "always_null" ] }
        },
        {
          "type": "function",
          "name": "Find",
          "returns": { "attributes": [ "maybe_null" ] }
        }
      ]
    }
  ]
}
```

### Как разметить пары аллокаторов/деаллокаторов

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

Рассмотрим простой пример:

```cpp
void my_free(void *);
void *my_unique_malloc(size_t);

void foo(size_t count)
{
  int *ptr = (int *) my_unique_malloc(count * sizeof(int)); 

  if (!ptr) return;

  my_free(ptr);            // <= V611
}
```

Аннотация:

```cpp
{
  "version": 5,
  "annotations": [
    {
      "type": "function",
      "name": "my_unique_malloc",
      "attributes": [ "memory_allocator" ]
    },
    {
      "type": "function",
      "name": "my_free",      
      "params": [
        {
          "type": "void *",
          "attributes": [ "pointer_to_free" ]
        }
      ]
    }    
  ]
}
```

Чтобы указать анализатору, что `my_unique_malloc` и `my_free` принадлежат одной логической группе, нужно добавить атрибут `alloc-group` с одинаковым тегом\. Например, используем тег `myAllocGroup`:

```cpp
{
  "version": 5,
  "annotations": [
    {
      "type": "function",
      "name": "my_unique_malloc",
      "attributes": [ "memory_allocator", "alloc-group(myAllocGroup)" ]      
    },
    {
      "type": "function",
      "name": "my_free",
      "attributes": [ "alloc-group (myAllocGroup)" ],    
      "params": [
        {
          "type": "void *",
          "attributes": [ "pointer_to_free" ]
        }
      ]
    }    
  ]
}
```

Теперь анализатор поймёт, что `my_unique_malloc` и `my_free` — пара, и ложных срабатываний диагностического правила [V611](https://pvs-studio.ru/ru/docs/warnings/v611/) не будет\.

Имя группы может быть любым; главное, чтобы оно было одинаковым для всех функций одной логической группы аллокации/деаллокации\.

**Примечание\.** Имя группы `stdlib` зарезервировано анализатором\. Этот тег служит для отнесения пользовательской функции к стандартной библиотеке \(`stdlib.h` для C, `cstdlib` для C\+\+\)\. Например:

```cpp
#include <cstdlib>

void *my_unique_malloc(size_t);

void foo(size_t count)
{
  int *ptr = (int *) my_unique_malloc(count * sizeof(int)); 

  if (!ptr) return;

  free(ptr);            
}
```

Если вы хотите, чтобы ваша функция аллокации считалась совместимой с `free` из стандартной библиотеки, достаточно добавить функцию в группу `stdlib`:

```cpp
{
  "version": 5,
  "annotations": [
    {
      "type": "function",
      "name": "my_unique_malloc",
      "attributes": [ "memory_allocator", "alloc-group (stdlib)" ]      
    }    
  ]
}
```