﻿# V2021\. Using assertions may cause the abnormal program termination in undesirable contexts\.

Данное диагностическое правило добавлено по просьбе пользователей\.

Диагностическое правило позволяет обнаружить в коде вызов макроса, который может привести к аварийному завершению программы\. Таким макросом может быть стандартный 'assert'\. Несмотря на то, что его использование позволяет устранять ошибки и снизить вероятность возникновения уязвимостей, его вызов может быть недопустим в различных сценариях\. Одним из таких вариантов может быть написание библиотечного кода\.

Рассмотрим следующий код:

```cpp
[[noreturn]] void assertHandler();
#define ASSERT(expr) (!!(expr) || (assertHandler(), 0))

void foo(int i)
{
  if (i < 0)
  {
    ASSERT(false && "The 'i' parameter must be non-negative");
  }
}
```

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

```cpp
[[noreturn]] void assertHandler();                      // N1
#define ASSERT(expr) (!!(expr) || (assertHandler(), 0))

//V_PVS_ANNOTATIONS annotations.json                    // N2
//V_ASSERT_CONTRACT, assertMacro:ASSERT                 // N3

void foo(int i)
{
  if (i < 0)
  {
    ASSERT(false);                                      // <= V2021
  }
}
```

На этом примере рассмотрим, как настраивается механизм распознавания пользовательского макроса\.

**Разметка функции как noreturn**\. Функция 'assertHandler', расположенная внутри макроса 'ASSERT', должна быть размечена как 'noreturn' \(строка N1\)\. Это можно сделать как при помощи стандартных атрибутов \([C23](https://en.cppreference.com/w/c/language/attributes) и [C\+\+11](https://en.cppreference.com/w/cpp/language/attributes)\):

```cpp
[[noreturn]] void assertHandler();              // since C23 or C++11
```

Так и специфичными для компилятора атрибутами \(например, [MSVC](https://learn.microsoft.com/en-us/cpp/cpp/noreturn?view=msvc-170) или [GCC / Clang](https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-noreturn-function-attribute)\):

```cpp
__declspec(noreturn) void assertHandler();      // MSVC
__attribute__((noreturn)) void assertHandler(); // GCC, Clang
```

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

```cpp
{
  "version": 1,
  "annotations": 
  [
    {
      "type": "function",
      "name": "assertHandler",
      "parameters": [],
      "attributes": [ "noreturn" ]
    },
    ....
  ]
}
```

Затем этот файл должен быть включён во время анализа одним из [описанных](https://pvs-studio.ru/ru/docs/manual/6743/#ID4A9FB06301) способов\. В примере это происходит в строке N2\.

**Разметка макроса**\. Необходимо, чтобы анализатор учитывал, что из\-за макроса 'ASSERT' выполнение кода может прерваться\. Делается это при помощи комментария на строке N3\. Более подробно об этом механизме можно узнать [здесь](https://pvs-studio.ru/ru/docs/manual/0040/#ID68ACC793A5)\.

## Задание имён функций, в которых допустим вызов макроса

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

```cpp
//-V2021_IGNORE_ASSERT_IN_FUNCTION, function: My::Qualified::Name
```

**Примечание**\. Данная диагностическое правило срабатывает также и на стандартный 'assert', и поэтому отключено по умолчанию, чтобы не выдавать большое количество срабатываний\.

Для того, чтобы включить диагностику, можно воспользоваться механизмом включения через [комментарий](https://pvs-studio.ru/ru/docs/manual/6630/#ID6C9B051901) или директивой '[\#pragma pvs](https://pvs-studio.ru/ru/docs/manual/0017/#IDBDB7967E63)'\.