﻿# V002\. Some diagnostic messages may contain incorrect line number\.

Анализатор обнаружил проблему, из\-за которой позиции предупреждений могут указывать на некорректные строки кода\. Это может происходить либо из\-за некорректной работы внешнего препроцессора, либо из\-за вставленных разработчиком директив '\#line' в исходный код\.

Анализатор PVS\-Studio для языков C и C\+\+ работает только с препроцессированными файлами, т\.е\. с файлами, в которых раскрыты все макросы \('\#define'\) и подставлены все включаемые файлы \('\#include'\)\. При этом в препроцессированном файле содержится информация о том, какие файлы куда подставились и в какие позиции\. Осуществляется это как раз с помощью директив '\#line', имеющих следующий формат:

```cpp
#line linenum "filename" // MSVC
# linenum "filename"     // GCC-like
```

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

Препроцессирование выполняется в любом случае\. Для пользователя эта процедура происходит незаметно\. Иногда препроцессор является частью анализатора кода, а иногда \(как в случае с PVS\-Studio\) используется внешний препроцессор\. Утилита анализа запускает компилятор, которым собирается проверяемый проект, для каждого проверяемого файла на языке C или C\+\+\. С его помощью генерируется препроцессированный файл с расширением '\*\.PVS\-Studio\.i'\.

Рассмотрим ситуацию, при которой происходит сбой в позиционировании предупреждений анализатора\. Речь пойдёт о написании директив '\#pragma' в несколько строк с применением лексемы продолжения предыдущей строки \('\\'\):

```cpp
#pragma \
  warning(push) 
void test()
{
  int a;
  if (a == 1) // V614 должно быть выдано здесь,
    return;   // а выдастся здесь
}
```

Компилятор MSVC [неверно формирует](https://godbolt.org/z/Eb45q1x3M) директивы '\#line' в препроцессированном файле при написании такого кода, при этом GCC и Clang делают всё верно\. Однако если немного изменить пример, то все внешние препроцессоры отрабатывают [корректно](https://godbolt.org/z/z3Y1rcccP):

```cpp
#pragma warning \
  (push) 
void test()
{
  int a;
  if (a == 1) // V614 теперь выдаётся корректно
    return;
}
```

Наша рекомендация —  это либо не использовать многострочные директивы '\#pragma', либо писать их так, чтобы они корректно обрабатывались внешним препроцессором\.

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

**Шаг N1**\. Анализатор открывает исходный файл и ищет самую последнюю лексему\. Выбираются только те лексемы, которые не короче трех символов\. Например, для следующего кода последней лексемой будет считаться 'return':

```cpp
1 #include "stdafx.h"
2
3 int foo(int a)
4 {
5   assert(   a >= 0
6          && a <= 1000);
7   int b = a + 1;
8   return b;
9 }
```

**Шаг N2**\. Найдя последнюю лексему, анализатор определит номер строки, в которой она находится\. В примере это строка под номером 8\. Далее анализатор ищет последнюю лексему в уже препроцессированном файле\. Если последние лексемы не совпадают, то, видимо, в конце файла раскрылся макрос\. В такой ситуации анализатор не сможет понять, корректно ли расположены строки\. Но подобное происходит крайне редко, и почти во всех случаях последние лексемы в исходном и препроцессированном файле совпадают\. Если это так, определяется номер строки, в которой расположена лексема в препроцессированном файле\.

**Шаг N3**\. После двух предыдущих шагов имеются номера строк, где расположена последняя лексема в исходном файле и в препроцессированном файле соответственно\. Если эти номера строк не совпадают, то произошёл сдвиг в нумерации строк\. И в данном случае анализатор сгенерирует предупреждение V002\.

**Примечание N1**\. Следует учитывать, что если некорректная директива '\#line' будет находиться в файле ниже всех найденных подозрительных участков кода, то все позиции выданных предупреждений будут корректны\. И хотя анализатор сгенерирует дополнительно предупреждение V002, это не помешает вам работать с результатами анализа\.

**Примечание N2**\. Хоть это и не ошибка непосредственно анализатора кода PVS\-Studio, тем не менее, это приводит к его некорректной работе\.

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

**Шаг N1**\. Перезапустите анализ решения/проекта/файла, сохранив промежуточные файлы анализа \(выключив настройку "Remove Intermediate Files"\)\.

**Шаг N2**\. Откройте отчёт в одном из плагинов для IDE\.

**Шаг N3**\. Отфильтруйте предупреждения согласно файлу, в котором произошёл сдвиг позиций\. Если производился анализ единичного файла, то фильтровать ничего не требуется\.

**Шаг N4**\. Отсортируйте предупреждения по номерам строк или позиций \(колонка 'Line' или 'Positions'\)\.

**Шаг N5**\. Найдите первое предупреждение, позиция которого смещена\.

**Шаг N6**\. Откройте препроцессированный файл, соответствующий исходному, с расширением '\*\.PVS\-Studio\.i'\.

**Шаг N7**\. Найдите строку, полученную на шаге N5, в препроцессированном файле\.

**Шаг N8**\. Двигайтесь вверх по препроцессированному файлу, начиная от полученной на шаге N7 позиции, и найдите первую ближайшую директиву '\#line'\.

**Шаг N9**\. В исходном файле перейдите на соответствующую строку, указанную в директиве '\#line', полученную на шаге N8\. Между этой строкой и строкой, на которой выдано предупреждение, находится код, который приводит к сдвигу\. Это могут быть многострочные вызовы макросов, многострочные директивы компиляторов и т\.д\.

Схематически работу алгоритма можно отобразить следующим образом:

![V002_ru/image2.png](https://import.viva64.com/docx/blog/V002_ru/image2.png)

Начиная с версии 7\.35 анализатор может сам вычислить приблизительную позицию начала сдвига\. В таком случае поиск проблемного фрагмента можно начинать с 6 пункта\. Чтобы включить механизм, добавьте специальный комментарий `//+V002,VERBOSE` в исходный файл или [файл конфигурации диагностик](https://pvs-studio.ru/ru/docs/manual/6630/)\. Подробнее об этом можно узнать в [документации](https://pvs-studio.ru/ru/docs/manual/6630/#V002_verbose)\.