Skip to content

Static_analysis

Static analysis tools examine source code without executing it to find bugs, style issues, and potential problems. They are essential for maintaining code quality.

Static analysis catches issues early:

  • Memory errors (buffer overflows, leaks)
  • Uninitialized variables
  • Null pointer dereferences
  • Race conditions
  • Style violations
  • Deprecated API usage

Modern, powerful C++ linter built on Clang.

Terminal window
# Ubuntu/Debian
sudo apt-get install clang-tidy
# macOS
brew install clang-tidy
# Or use LLVM releases
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.1/clang+llvm-17.0.1-x86_64-linux-gnu-ubuntu-22.04.tar.xz
Terminal window
# Analyze a single file
clang-tidy main.cpp -- -std=c++17
# Analyze entire project
clang-tidy src/*.cpp -- -std=c++17 -I./include
# Fix some issues automatically
clang-tidy main.cpp --fix -- -std=c++17
# Fix and apply changes
clang-tidy main.cpp --fix-errors -- -std=c++17

Create .clang-tidy in project root:

---
Checks: >
clang-diagnostic-*,
clang-analyzer-*,
modernize-*,
performance-*,
readability-*,
-modernize-use-trailing-return-type,
-readability-magic-numbers,
CheckOptions:
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.functionCase
value: lower_case
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
// modernize-use-nullptr
void oldStyle() {
char* p = NULL; // Bad
char* p2 = nullptr; // Good
}
// modernize-use-emplace
void bad() {
std::vector<std::string> v;
v.push_back("hello"); // Creates temporary
}
void good() {
std::vector<std::string> v;
v.emplace_back("hello"); // Constructs in place
}
// modernize-range-based-for
void old() {
std::vector<int> v = {1, 2, 3};
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << std::endl;
}
}
void modern() {
std::vector<int> v = {1, 2, 3};
for (const auto& x : v) {
std::cout << x << std::endl;
}
}
// performance-unnecessary-value-param
void bad(const std::vector<int> v) { // Copies!
// Use: const std::vector<int>& v
}
// performance-move-const-arg
void bad(std::vector<int> v) {
// If v is passed by value, use std::move
}
void good(std::vector<int> v) {
process(std::move(v));
}
# Enable clang-tidy in CMake
set(CMAKE_CXX_CLANG_TIDY
clang-tidy
-header-filter='.*'
-checks='clang-analyzer-*,performance-*,readability-*'
)

Or run separately:

Terminal window
# Generate compile_commands.json
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
# Run clang-tidy on all files
clang-tidy -p build/compile_commands.json src/*.cpp

Lightweight, focused on memory errors and common mistakes.

Terminal window
# Ubuntu/Debian
sudo apt-get install cppcheck
# macOS
brew install cppcheck
Terminal window
# Check all files in project
cppcheck --enable=all src/
# Enable specific checks
cppcheck --enable=memory,unusedFunction src/
# Quiet mode (only errors)
cppcheck --quiet src/
# XML output
cppcheck --xml --enable=all src/ 2> errors.xml
CategoryDescription
errorBugs and memory errors
warningPotential bugs
styleStyle issues
portabilityCross-platform issues
unusedFunctionUnused functions
missingIncludeMissing headers
// Memory errors
int arr[10];
arr[10] = 5; // Array index out of bounds
int* p = new int[10];
delete p; // Should be delete[] p
// Uninitialized variables
int x;
std::cout << x; // Uninitialized read
// Null pointer
int* p = nullptr;
*p = 5; // Null pointer dereference
// Resource leaks
void leak() {
FILE* f = fopen("file.txt", "r");
// Missing fclose(f)
}
<!-- suppressions.txt -->
// Suppress specific warnings
*:/src/third_party/*.cpp
unmatchedSuppression
Terminal window
cppcheck --suppressions-list=suppressions.txt src/
static-analysis:
stage: test
script:
- cppcheck --enable=all --inconclusive --xml --output-file=cppcheck.xml src/
artifacts:
reports:
static_analysis: cppcheck.xml

Ensures proper header includes.

Terminal window
# Ubuntu
sudo apt-get install include-what-you-use
# macOS
brew install include-what-you-use
Terminal window
# Basic usage
include-what-you-use main.cpp -- -std=c++17
# With CMake
include-what-you-use -Xiwyu --verbose=3 main.cpp -- -std=c++17 -I./include
main.cpp
#include <vector> // IWYU: add #include <utility> for std::move
#include <string>
void foo() {
std::vector<std::string> v;
v.emplace_back("test");
}

Output:

main.cpp:1:1: add include at the top of the file (for std::move) [iwyu]

Enterprise-grade code quality platform.

Terminal window
docker run -d --name sonarqube -p 9000:9000 sonarqube:latest
# sonar-scanner.properties
sonar.projectKey=myproject
sonar.sources=src
sonar.cxx.clangtidy.reportPaths=clang-tidy-report.txt
sonar.cxx.cppcheck.reportPaths=cppcheck-report.xml
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}

Commercial static analyzer for Windows, Linux, macOS.

Terminal window
# Download from https://pvs-studio.com/order/open-source-license/
# Linux:
wget https://files.pvs-studio.com/files/pvs-studio-7.24.49414.deb
sudo dpkg -i pvs-studio-7.24.49414.deb
Terminal window
# Analyze compile_commands.json
pvs-studio-analyzer analyze -o pvs.log -C compile_commands.json
# Convert to other formats
pvs-studio-html-report pvs.log -o pvs-report.html
pvs-studio-junit-simple pvs.log -o pvs-junit.xml
find_package(PVSStudio)
if(PVSStudio_FOUND)
add_custom_target(pvs-studio ALL
COMMAND pvs-studio-analyzer analyze
-o ${CMAKE_BINARY_DIR}/pvs.log
-C ${CMAKE_EXPORT_COMPILE_COMMANDS}
COMMENT "Running PVS-Studio analyzer..."
)
endif()
Terminal window
# Ubuntu
sudo apt-get install llvm
Terminal window
# Compile with coverage
clang++ -fprofile-instr-generate -fcoverage-mapping -o test test.cpp
# Run to generate .profraw
./test
# Generate coverage report
llvm-profdata merge -o coverage.profdata default.profraw
llvm-cov show ./test -instr-profile=coverage.profdata
# HTML report
llvm-cov report ./test -instr-profile=coverage.profdata \
-show-regions -show-line-counts-or-regions
- name: Test and Coverage
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON
cmake --build build
ctest --output-on-failure
name: Static Analysis
on: [push, pull_request]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install clang-tidy
run: sudo apt-get install clang-tidy
- name: Run clang-tidy
run: |
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
find src -name "*.cpp" -exec clang-tidy -p build/ {} \;
- name: Run cppcheck
run: |
sudo apt-get install cppcheck
cppcheck --enable=all --inconclusive src/
stages:
- analyze
clang-tidy:
stage: analyze
image: ubuntu:22.04
before_script:
- apt-get update && apt-get install -y clang-tidy cmake build-essential
- cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
script:
- find src -name "*.cpp" -exec clang-tidy -p build/ {} \;
allow_failure: true # Warnings don't fail CI
cppcheck:
stage: analyze
image: ubuntu:22.04
before_script:
- apt-get update && apt-get install -y cppcheck
script:
- cppcheck --enable=all --inconclusive --xml src/ 2> cppcheck.xml
artifacts:
reports:
static_analysis: cppcheck.xml
  1. Run in CI: Integrate static analysis in your pipeline
  2. Fix warnings: Don’t let warnings accumulate
  3. Configure wisely: Tune checks for your codebase
  4. Start simple: Enable core checks first, add more gradually
  5. Use multiple tools: Different tools catch different issues
  6. Automate fixes: Use —fix for auto-fixable issues
  7. Baseline: Create exceptions for existing issues, fix new ones
ToolFocusOpen SourcePerformance
clang-tidyModern C++YesFast
cppcheckMemory errorsYesMedium
IWYUIncludesYesFast
SonarQubeQualityPartialSlow
PVS-StudioGeneralCommercialMedium
  • Static analysis catches bugs early without execution
  • clang-tidy provides modern C++ recommendations
  • cppcheck focuses on memory errors
  • IWYU ensures proper includes
  • Integrate tools in CI/CD for consistent quality
  • Combine multiple tools for comprehensive coverage