The analyzer has detected an issue that may cause warning messages to point to incorrect code lines. This can happen either due to incorrect external preprocessor operation or because of '#line' directives added to the source code by the developer.
The PVS-Studio analyzer for C and C++ works only with preprocessed files, i.e. with files that have all macros expanded ('#define') and all included files substituted ('#include'). The preprocessed file also contains information about the substituted files and their positions. This is done using the '#line' directives that look like this:
#line linenum "filename" // MSVC
# linenum "filename" // GCC-like
The line following the directive in the preprocessed file is interpreted as coming from the 'filename' file and having the 'linenum' number. So, in addition to the code ready for the analysis, preprocessed files also contain information about which file each fragment came from.
Preprocessing is performed in any case. The process is invisible to the user. The preprocessor can be a part of the code analyzer or be external (as in the PVS-Studio case). For each C or C++ file being checked, the analysis utility runs the compiler that builds the project being analyzed. It is used to create a preprocessed file that has the '*.PVS-Studio.i' extension.
Let's look at a case where the positioning of analyzer warnings fails. This concerns writing '#pragma' directives across multiple lines using the line continuation character ('\'):
#pragma \
warning(push)
void test()
{
int a;
if (a == 1) // V614 should be issued for this line,
return; // but it's issued here
}
The MSVC compiler incorrectly creates '#line' directives in the preprocessed file when compiling such code, while GCC and Clang do it correctly. However, if we modify the example a bit, all external preprocessors operate correctly:
#pragma warning \
(push)
void test()
{
int a;
if (a == 1) // V614 is issued correctly now
return;
}
We recommend either to avoid multi-line '#pragma' directives at all, or write them in such a way that they are handled correctly by an external preprocessor.
The analyzer attempts to detect a line shift in the preprocessed file and alert the user about it by issuing the V002 warning. However, it does not try to fix the positions of the issued warnings in the code. The algorithm for finding line shifts works as follows.
Step N1. The analyzer opens the source file and searches for the last token. It selects only those tokens that are longer than three characters. For example, in the following code, the last token would be 'return':
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 }
Step N2. After finding the last token, the analyzer determines its line number. In the example, this is line number eight. Next, the analyzer searches for the last token in the preprocessed file. If the last tokens do not match, the macro must have expanded at the end of the file. In this case, the analyzer cannot determine whether the lines are arranged correctly. However, this rarely happens, and almost always the last tokens in the source and preprocessed files match. If so, the line number where the token is located in the preprocessed file is determined.
Step N3. After completing the previous two steps, we have the line numbers of the last token in the original file and in the preprocessed file, respectively. If these line numbers do not match, a shift in line numbering has occurred. In this case, the analyzer issues the V002 warning.
Note N1. Note that if the incorrect '#line' directive is located below all the suspicious code fragments found in the file, then all the warning positions are correct. Even though the analyzer issues the V002 warning, it does not prevent you from handling the analysis results.
Note N2. Although this is not an error in the PVS-Studio code analyzer, it does lead to its incorrect operation.
If you want manually find the line that caused the shift in the source file, you can use the following algorithm:
Step N1. After saving the intermediate analysis files (by disabling the "Remove Intermediate Files" setting), restart the solution/project/file analysis.
Step N2. Open the report in one of the IDE plugins.
Step N3. Filter the warnings by the file where the position shift occurred. If you want to analyze a single file, there is nothing to filter.
Step N4. Sort warnings by line or position number (the 'Line' or 'Positions' column).
Step N5. Find the first warning which has a shifted position.
Step N6. Open the preprocessed file with the '*.PVS-Studio.i' extension that corresponds to the original file.
Step N7. In the preprocessed file, find the line identified in the N5 step.
Step N8. Starting from the position obtained in the N7 step, move up the preprocessed file and find the first closest '#line' directive.
Step N9. In the source file, go to the line specified in the '#line' directive obtained in the N8 step. The code that causes the shift is located between this line and the line where the warning was issued. These can be multi-line macro calls, multi-line compiler directives, etc.
This is a schematic overview of how the algorithm works: