
Another story, how difficult it is for programs to interact with the outside world. At first glance, the static analyzer should have no problems. It receives files for input, additional information and must generate a report. But as always, the devil is in the details.
I consider PVS-Studio a very high quality product. We can make and upload a distribution kit almost any day. We use a very large number of automated tests of various levels and types. Here is a description of some of them: "
How we test the code analyzer ." Now there are more of them. For example, now for static analysis we use not only our own analyzer, but also Clang. If the revised version has passed all the tests, then it can be safely issued to users.
Unfortunately, all the beauty and reliability of the internal code sometimes falls apart due to the impacts of a hostile environment. As a result, the entire impression of the product spoils. It seems we are not to blame, but it is our product that does not work. I can cite a large number of examples. The first thing that comes to mind is:
- Third-party add-in spoils something in the Visual Studio environment. As a result, you have to create a crutch to work around a problem, or accept it and respond, "sorry, we can do nothing." One such example is " Description of the Intel Parallel Studio Service Pack 1 Integration Error in Visual Studio 2005/2008 ."
- Visual Studio COM interfaces for project information may unexpectedly throw an exception. Apparently, the environment at this unfortunate moment is busy with something else. Challenges have to be wrapped in a loop for multiple repetitions. Dances with a tambourine, which do not always help.
- A developer has antivirus X and, according to corporate policy, he does not have the right to change his settings. This antivirus "holds" for some time some temporary files, and the analyzer cannot delete them. The result is that the analyzer "shits" in the project folder.
- Miscellaneous can be remembered. Some examples can be found here , here and here .
Now I will tell another such story, how easy it is to spoil the impression of our product, being innocent.
')
One of the potential users sent a question about the strange behavior of PVS-Studio:
We are now chasing the trial version and are thinking about buying the full one. However, the analysis stumbled upon one nuance that puts the validity of the conclusions into question.Below is a screenshot showing the error.filePath and cachePath are marked as unused (warning V808 ), although it can be seen that they are used literally in the next line after the announcement.Could you explain this behavior of the analyzer?In the screenshot you can see the code of the following type (code changed):
std::string Foo() { std::string filePath(MAX_PATH + 1, 0); std::string cachePath = "d:\\tmp"; if (!GetTempFileName(cachePath.c_str(), "tmp", 0, &filePath.front())) throw MakeSystemError("...", GetLastError(), __SOURCE__); return std::move(filePath); }
What can I say. Shame on the analyzer. After all, really reports nonsense. The variables filePath and cachePath are explicitly used. There are no reasons for warning. And okay, the function would be on 1000 lines. No, the function is simple for disgrace.
Everything, the first impression is spoiled. Now I will tell about the results of the investigation.
PVS-Studio analyzer uses the Visual C ++ compiler (CL.exe) or Clang to preprocess files. In more detail, how we use Clang is described in the article: "
A little about the interaction of PVS-Studio and Clang ".
The Visual C ++ compiler preprocessor works efficiently, but very slowly. Clang is fast, but a lot of things don't support or work incorrectly. Clang developers say they are very well compatible with Visual C ++, but this is not true. There are many little things that they do not support or do not like Visual C ++. For the analyzer, these little things are fatal, as happened this time.
By default, the PVS-Studio analyzer first tries to preprocess the file with Clang. However, he knows: Clang is far from always able to preprocess what Visual C ++ can. If a preprocessing error occurs, CL.exe is launched. So a little time is lost on the useless launch of Clang, but in general this approach saves time on getting * .i files.
In this case, it did not help. Clang “successfully” preprocessing the file, although the output is abracadabra.
The reason for the incorrect behavior was the macro __SOURCE__, declared in the code as follows:
#define __SLINE_0__(_line) #_line #define __SLINE__(_line) __SLINE_0__(_line) #define __SOURCE__ __FILE__":"__SLINE__(__LINE__)
When preprocessing a line:
throw MakeSystemError(_T("GetTempFileName"), GetLastError(), __SOURCE__);
It should turn into:
MakeSystemError("GetTempFileName", GetLastError(), "..path.."":""37");
This is exactly what the Visual C ++ compiler does. All perfectly. The analyzer will correctly process this code.
If you explicitly use CL.exe in the settings of PVS-Studio, then false positives will disappear. However, if Clang is launched, the analyzer will deal with incorrect code.
Clang cannot master macros and at the output we have:
throw MakeSystemError("GetTempFileName", GetLastError(), "..path.."":"__SLINE__(37));
The macro __SLINE__ remained undiscovered.
It turned out an incorrect construction, which is unacceptable from the point of view of the C ++ language. Having encountered it, the PVS-Studio analyzer tries to somehow bypass the incorrect code in order to continue the analysis further. It is better to miss something, than not to completely process the file. Often, such omissions have no effect on the results of the analysis.
But this time around the incorrect place painlessly failed. The whole block was thrown:
if (!GetTempFileName(cachePath.c_str(), "tmp", 0, &filePath.front())) throw MakeSystemError("....", GetLastError(), __SOURCE__); return std::move(filePath);
So it happened ... The analyzer did everything it could.
Since this fragment does not exist for the analyzer, the variables are not initialized, are not used in any way. This leads to the issuance of a false positive.
One solution to the problem is to always use a preprocessor from Visual C ++. But there is a drawback - slow analysis.
Therefore, in this case, we have chosen a different path. Since the company from which we were approached, is thinking about acquiring PVS-Studio, we considered this particular case and made another backup in the code. It is ugly, but practical. We already have a lot of different special places that bypass some of the nuances found in the projects of our users. This is a form of support.
So, this time we were let down by the preprocessor implemented in Clang. Interestingly, what will be the reason to write the following similar article on external errors.
That's how we live. Thanks for attention.
Try our static code analyzer on your projects and, if something goes wrong,
write to us . Often we turn a negative attitude into a positive one.