Verone - static analyzer for C ++ with on-the-fly analysis

My name is Vladimir Plyashkun and in today's article I am going to introduce a free Verone static analyzer for C ++ 03 / C ++ 11 / C ++ 14, the main feature of which is on-the-fly analysis.

Today, there is a large selection of static analysis tools for the C ++ language: Coverity / Klocwork / Parasoft / Clang / CppCheck / PVS-Studio and many others. And recently ReSharper C ++ was released.

Nevertheless, the task of analysis on the fly is still relevant, because there are practically no solutions with this feature. The same ReSharper, although it integrates with Visual Studio and finds many problems online (for example, unused header files), but it is still seen more as a refactoring tool than the classical static analyzer. R # has virtually no diagnostics for semantic errors, copy-paste errors and typos, as well as runtime errors (memory leaks, buffer overflows, dereferencing of null pointers, etc.)

Coverity / Klocwork / Parasoft - each of these tools calls itself a market leader, but it will be extremely difficult for an individual developer to get even a trial version of these analyzers, because the target orientation of these products is different and is aimed at large companies. Therefore, it makes no sense to consider them.

Clang is mainly aimed at finding vulnerabilities in the source program. Checks available here .

It is disappointing that clang practically does not diagnose typos and semantic errors, and using clang at the moment outside of Xcode is somewhat inconvenient. Of course, the analyzer can be launched from the console, but such a scheme requires manual configuration (specifying a list of files, paths to directories of header files, predefined macros, etc.). And therefore, such a periodic launch of an external utility outside the IDE can hardly be called convenient and useful.

PVS-Studio went a little further and integrated into the build system. Now analysis is performed automatically after each build of the project. But again, the question arises, why not run the analysis on the fly, highlighting all the errors found right during the encoding? Which is much more logical and convenient for the end user.

This is precisely the problem that the Verone analyzer solves. After installation, the analyzer will automatically start when you open Visual Studio. In the background thread, Verone will track the changes that the programmer makes during the work, analyze them, and display the result of the problems found. All diagnostics for the current file are indicated to the right of the editor in a special indent: The



color of the so-called glyph depends on the type of diagnostics (note, warning, error). To see all the problems found, just click on the analyzer icon on the toolbar, after which a window with all the problems detected will appear:



At the moment, the analyzer has about 20 diagnostics aimed at finding semantic errors and typos. Of course, this is extremely small to compete with existing tools, but it’s worth understanding that this is only the first release and many developments have simply not been included in it. Future plans will be at the end of the article. Nevertheless, this is already enough to identify defects in real projects.

For example, the following problems were found in the LLVM source code:

else if (ArgTy->isStructureOrClassType())
    return record_type_class;
else if (ArgTy->isUnionType())   //<---
    return union_type_class;
else if (ArgTy->isArrayType())
    return array_type_class;
else if (ArgTy->isUnionType())  //<----
    return union_type_class;

Here is the condition

ArgTy->isUnionType()

repeated twice due to banal carelessness. And if this fragment can hardly be considered a serious bug, then this code already causes much more suspicion:

if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_UNDEFINED_LAZY)  //<----
    outs() << "undefined [lazy bound]) ";
else if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_UNDEFINED_LAZY)  //<---
    outs() << "undefined [private lazy bound]) ";
else if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY)
    outs() << "undefined [private]) ";
else
    outs() << "undefined) ";

The second block will never receive control, since its condition is completely identical to the condition of the first block. After studying the source code, I can assume that in the second case there should be a condition of the form:

(NDesc & MachO::REFERENCE_TYPE) == MachO:: REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY

A similar problem was discovered by the libsass project.

if (lhs_tail->combinator() != rhs_tail->combinator()) return false;
if (lhs_tail->head() && !rhs_tail->head()) return false;
if (!lhs_tail->head() && rhs_tail->head()) return false;
if (lhs_tail->head() && lhs_tail->head()) {  //<---
    if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) 
        return false;
}

In this fragment, the last condition is erroneous:

lhs_tail->head() && lhs_tail->head()

Again, the code is written in such a way that the error did not make itself felt, but this does not mean that it does not need to be fixed, which was done by the authors in one of the last commits.

Many suspicious spots were found in the Torque 3D graphics engine.

// Update accumulation texture if it changed.
// Note: accumulation texture can be NULL, and must be updated.
if ( passRI->accuTex != lastAccuTex )
{
    sgData.accuTex = passRI->accuTex;
    lastAccuTex = lastAccuTex;
    dirty = true;
}

Based on the logic, the lastAccuTex (GFXTextureObject *) pointer refers to the last processed passRI-> accuTex object, but due to the carelessness of the variable, its own value is assigned.

And in this fragment:

class ConnectionProtocol
{
public:
   ConnectionProtocol();
   virtual void processRawPacket(BitStream *bstream);
   virtual Net::Error sendPacket(BitStream *bstream) = 0;
   virtual void keepAlive() = 0;
   virtual void handleConnectionEstablished() = 0;
   virtual void handleNotify(bool recvd) = 0;
   virtual void handlePacket(BitStream *bstream) = 0;
	...
};

The class is polymorphic, but has a default destructor (not virtual). Therefore, this can lead to resource leaks in child classes if a pointer to the base class is used during deletion.

There are suspicious places with integer division that discard the fractional part, as for example here:

bm->avgfloat=PACKETBLOBS/2

The variable bm-> avgfloat is of floating-point type, but since integer division is to the right of the assignment operator, the fractional part will be discarded. Not to say that this is a clear mistake, but it requires attention.

In conclusion, it is worth saying that the development of a static analyzer is a very big task that requires a lot of resources and time. There are many developments that are not included in this release. Therefore, I would like to describe several areas in which Verone will develop in the next six months or a year.

  1. A more advanced analysis algorithm on the fly, based on the generation of mock files (thanks to Sasha Kokovin for the idea)
    Now this algorithm works extremely simply. In fact, everything is tied around a file with the pairs <file name> - <date of last analysis> . And the analyzer core just starts at a certain frequency. Since a small number of files change during the work, this scheme still allowed us to scale to large projects.
  2. Ability to analyze changes that have not yet been saved in the editor.
    The above scheme, unfortunately, is tied to files and does not take into account changes that have already been made in the editor but have not yet been saved. This task is quite voluminous and requires careful testing, and therefore was not implemented in the first release.
  3. Analysis of runtime errors
    One of the main directions is the analysis of runtime errors, which is not in the current version. Runtime error analysis, coupled with on-the-fly analysis, looks very promising, because, in my opinion, seeing, for example, dereferencing a null pointer at the moment of coding, is very cool! Of course, some simplifications will have to be made, because the data flow analysis algorithms are quite resource intensive. But even, for example, local analysis will still bring tangible benefits, warning the developer about possible problems.

Of the more distant tasks, it is also worth highlighting:

  1. Improving the graphical interface
    Now there is no functionality for hiding diagnostics, searching for them and saving them to a file, which would be useful in everyday work.
  2. Mastering other IDEs and operating systems
  3. Standalone application A
    standalone graphical application is useful because not riveted to any development environment. Already now you can safely use the console version of the analyzer, but the version with a graphical interface seems to be a more convenient solution for this purpose.

The analyzer is available for download here . At the moment, the analyzer is available only for Windows, a version for integration with Visual Studio 2015 is available. Versions for Visual Studio 2012 and Visual Studio 2013 will appear in a week or two.

Ready to answer any questions. I will be glad to feedback and suggestions!

Also popular now: