Static Analysis for C++ with CMake and PVS-Studio
Static analysis is one of the most practical ways to improve the quality of a C++ codebase before bugs escape into reviews, releases, or user reports. This matters in any project, but it matters even more in open source, where code is maintained over time by contributors with different levels of familiarity with internal invariants, parser assumptions, and platform-specific behavior.
CMake offers a variety of tools for directly integrating static checks during compilation, including support for the PVS-Studio static analyzer starting in CMake 4.3. PVS-Studio offers a wide array of configurable error-codes for C++, and can help expose potential errors and vulnerabilities in your projects. You can read more about the tool and its new CMake integration in this post on PVS-Studio official blog.
Using CMake for C++ Static Analysis
CMake 4.3 introduces new <LANG>_PVS_STUDIO properties for C and C++ to enable PVS-Studio static checks at the target level, as well as a CMAKE_<LANG>_PVS_STUDIO variable to provide a default global setting. This property should be set to the desired command-line invocation for the pvs-studio-analyzer tool.
#run pvs-studio-analyzer analyze -a GA OP on our foo.cxx source file
if (Foo_ENABLE_PVS_STUDIO_CHECKS)
# on windows, the program is called CompilerCommandsAnalyzer
find_program(PVS_STUDIO_COMMAND NAMES pvs-studio-analyzer)
set(CMAKE_CXX_PVS_STUDIO ${PVS_STUDIO_COMMAND} analyze -a "GA\;OP")
endif()
add_executable(foo foo.cxx)
Alternatively, this setting can be enabled on the command line with -DCMAKE_CXX_PVS_STUDIO=… without editing a project’s CMake files.
This workflow matches the existing interfaces for static analysis checks in CMake, which include:
- iwyu (include what you use): This parses C++ source files and determines the exact include files required to compile that file, no more, no less.
- clang-tidy: is a clang-based C++ “linter” tool. Its purpose is to provide an extensible framework for diagnosing and fixing typical programming errors, like style violations, interface misuse, or bugs that can be deduced via static analysis. clang-tidy is modular and provides a convenient interface for writing new checks.
- lwyu (link what you use): This is a built in CMake feature that uses options of ld and ldd to print out if executables link more libraries than they actually require.
- cpplint: a C++ style checker following Google’s C++ style guide.
- cppcheck: a static analysis tool for C/C++ code. Cppcheck primarily detects the types of bugs that the compilers normally do not detect. The goal is to detect only real errors in the code (i.e., have zero false positives).
You can read more about the existing static analysis support in older CMake versions in this post.
By creating option-guarded settings for PVS-Studio analysis in project code, authors can create standardized settings for linting C++ code, which can be easily reproduced by both developers and in CI.
Linting CMake’s own source with PVS-Studio
A recent change to CMake’s own CI includes running PVS-Studio to lint C++ source files in a nightly job on staged merged requests. An option defined in the top-level CMakeLists.txt enables searching for the pvs-studio-analyzer tool and specifies a basic set of arguments to run across its source files. On a first pass, this gave us errors from the PVS-Studio analyzer in our dashboard.


Writing a global .pvsconfig file allows us to suppress the existing warnings, so that we can keep new errors out of the codebase while we address the current set of warnings. We’re already working to trim down the set of errors reported by PVS-Studio, allowing us to shrink the list of ignored errors incrementally.

PVS-Studio checks in CMake’s nightly CI at open.cdash.org
So far, the warnings we’ve detected have not represented reachable undefined behavior or user-facing bugs. However, the checks do find unguarded dereferencing, possible out-of-bounds errors, and unsafe buffer manipulation that could lead to bugs in the future if the code is modified or accessed without a complete picture of how the existing logic flow prevents such failures.
These are the kinds of issues that often sit quietly in a codebase until a later change, a new input shape, or an unexpected internal state makes them observable. That is why they are worth fixing even when they do not map neatly to a reproducible failure. For open source projects with many contributors, these kinds of checks have the potential to catch critical bugs before they get merged to master.
The most common failing checks in CMake’s codebase included:
- V730 – not all class members are initialized in a constructor or generated constructor
- V539 – suspicious iterator passed to erase
- V547 – expression is always true or always false
- V560 – part of a conditional expression is always true or always false
- V508 – suspicious new type(n) usage, likely meant new type[n]
- V1023 – raw pointer inserted into a container without ownership safety
- V1037 – multiple case branches perform the same actions
- V519 – variable assigned twice in succession
- V506 – pointer to a local variable escapes its scope
- V1004 – pointer used unsafely after a null check
This new integration provides one more way CMake’s own open source code is linted and verified, and one more way that other projects using CMake can easily add such verification to their own pipelines.