Blog 3 min read

C++ Static Analysis: Bugs vs. Bug-Prone Situations

Share this article
C++ Static Analysis: Bugs vs. Bug-Prone Situations

Static analysis is not only about finding bugs directly, but also about identifying bug-prone situations that can harm code readability and maintainability. Static analysis can handle many other properties of the code:

  • Code metrics: for example, methods with too many loops, if, else, switch and case statements end up being hard to understand, hence hard to maintain. Counting these through the code metric Cyclomatic Complexity is a great way to assess when a method becomes too complex.
  • Dependencies: if the classes of your program are entangled, the effects of any changes in the code become unpredictable. Static analysis can help to assess when classes and components are entangled.
  • Immutability: types that are used concurrently by several threads should be immutable; otherwise you’ll have to protect state read/write access with complex lock strategies that will end up being unmaintainable. Static analysis can make sure that some classes remain immutable.
  • Dead code: dead code is code that can be removed safely, because it is not invoked anymore at runtime. Not only can it be removed, but it must be removed, because this extra code adds unnecessary complexity to the program. Static analysis can find most of the dead code in your program (yet not all).
  • API breaking change: if you present an API to your client, it is very easy to remove a public member without noticing and thus break your clients’ code. Static analysis can compare two states of a program and warn about this pitfall.
  • API usage: some APIs are intended to be used carefully. For example, a class that holds disposable fields must generally be disposable itself, except when the disposable field’s lifetime is not aligned with the class instance’s lifetime — which then smells like a design problem.

Many useful tools exist for detecting bugs in your C++ codebase, such as Cppcheck, Clang, and the Visual Studio analyzer. But what about identifying bug-prone situations?

While static analysis tool creators can decide which situations are considered bugs, that’s not the case for bug-prone situations, which depend on the development team’s choices. For example, one team could consider that a method with more than 20 lines is complex, while another team could set the maximum at 30. If a tool detects bug-prone situations, it must also provide a way to customize the relevant rules.

Code as Data Is the Best Way to Detect Bug-Prone Situations

Static analysis is the idea of analyzing source code  for various properties and reporting on those properties, but it’s also, philosophically, the idea of treating code as data.  This is deeply weird to us as application developers, since we’re very much used to thinking of source code as instructions, procedures, and algorithms.  But it’s also deeply powerful.

After analyzing a source file, we can extract its AST and generate a model containing a wealth of useful information about the code. We can then query this model using a code query language similar to SQL.

CppDepend provides a powerful code query language named CQLinq to query the code base like a database. Developers, designers, and architects can define custom queries to easily identify bug-prone situations.

With CQLinq, we can combine data from code metrics, dependencies, API usage, and other model information to define very advanced queries that match specific bug-prone situations.

Here’s an example  of a CQLinq query that matches the most complex methods:

bugs

Summary

It’s better to combine several C++ tools to detect problems in your codebase: some tools detect bugs, while others also detect bug-prone situations. With CppDepend, we aim to combine the strengths of multiple tools: we provide an easy way to define custom queries, and we also recently added a feature to import results from other static analysis tools and query them with CQLinq.

Share this article