Static analysis and the LLVM toolchain#
Static analysis can run locally. In the maintainer’s workspace, solver builds for qualification, solver test batches, fuzzing, benchmarks and corpus validation belong on CPU SLURM; contributors may use a suitable machine. The commands below configure CMake and parse source; they do not build or run Primo. Configuration can compile small compiler/dependency capability probes.
Run a complete source scan#
Install matching Clang, clang-tidy and clang-format tools, CMake, ripgrep, and
the normal development dependencies under contrib/local. The defaults use
LLVM 18. The distribution’s run-clang-tidy-18 also needs PyYAML in the Python
interpreter that runs it; the default is /usr/bin/python3. The three Primo
Python helpers themselves use only the standard library.
make lint
make tidy STATIC_JOBS=2
make include-cleaner STATIC_JOBS=2
make check-headers STATIC_JOBS=2
Each static target creates a timestamped directory under results/. Set
STATIC_REPORT_DIR=results/my-static-attempt to choose a durable name, using
a fresh name for each attempt. Override STATIC_CXX, CLANG_TIDY,
RUN_CLANG_TIDY, and STATIC_BUILD_DIR when the installed paths differ.
STATIC_JOBS bounds concurrent analyzer/compiler processes; it does not impose
a memory limit. For example, in Bash, a scan can inherit an 8 GiB per-process
address-space limit and a 30-minute overall deadline:
(ulimit -v 8388608; timeout -k 10s 30m make tidy STATIC_JOBS=2)
The Makefile static targets depend on analysis-db, which configures
a separate Debug compilation database and generates the Git revision metadata
header needed by main.cpp. There is no dependency on build or dependency
installation. A missing compiler, analyzer or development dependency fails the
command rather than skipping the check.
prepare_static_analysis.py
requires actual CMake commands for every tracked production and unit-test
.cpp file. It supplies explicit derived commands for documentation examples
using the SMT library’s flags, and for the standalone rational benchmark using
rational.cpp’s flags. These derived contexts are recorded individually; they
are parsing contexts, not evidence that those programs were built or linked.
All command variants are preserved. The manifest records hashes of the input
database, owned C++ sources/headers and generated revision header. The original
database digest is provenance; the summarizer does not rehash that database.
Read the result#
The clang-tidy profile enables compiler diagnostics,
Clang Static Analyzer, bugprone, performance, portability and include-cleaner
checks. The header filter requests owned source, tests and examples; analyzer
path reports can still point into dependencies, which retain their own paths
in the inventory.
make include-cleaner provides a separate include scan. Both targets reject
redundant includes through an explicit gate; missing-direct-provider advice
remains advisory. Neither target applies suggested edits.
The LLVM runner writes tidy.log and tidy.yaml; the status and diagnostic
inventory is tidy-summary.json. The include-only equivalents are
includes.log, includes.yaml and includes-summary.json. A nonzero aggregate
exit must be treated as incomplete even if a selected file later passes a
retry. summarize_static_analysis.py
requires a successful aggregate, an observed command for every expected source,
unchanged recorded C++ sources, owned headers and generated revision header,
and no compiler errors. It deduplicates repeated
header diagnostics by location, message and check. Its statuses are:
Status |
Meaning |
Target result |
|---|---|---|
|
Complete scan with no reported diagnostics |
Success |
|
Complete scan with diagnostics requiring review |
Success if the unused-include gate also passes |
|
Failed tool, missing source, changed recorded source/header/revision or compiler error |
Failure |
Most of the broad profile is advisory while establishing a baseline. Both
static targets also set --reject-unused-includes: an unused_include_gate
object in the summary records blocking diagnostics and retained paired headers.
An implementation’s own header is retained only when supported quoted-include
search resolves to the same .hpp/.h stem in every recorded compile context.
Source-relative lookup precedes -iquote, -I, -isystem and -idirafter;
shadowing headers are rejected, and unsupported search flags receive no
exemption. Other unused includes fail the target even when analysis completed.
The dated triage explains the 20 current
paired anchors and the separate missing-direct-include backlog. A successful
target is therefore not a warning-free gate. Formatting and Ruff remain
blocking in make lint. Review diagnostics in their owning function:
check guards and caller invariants, allocation and invalidation boundaries,
exception guarantees, and test setup. In particular, a proposed reference in
place of a copy needs a lifetime argument across intervening mutations. Keep
paired header self-includes unless their removal is justified independently.
check_static_headers.py generates a
one-include translation unit for each tracked header under src/, using every
SMT-library compile context with -fsyntax-only. It records per-header commands,
stdout/stderr and exits, with a 60-second timeout per check. This detects
accidental dependence on include order; it does not prove that all template
instantiations or feature configurations compile.
Coverage and remaining checks#
Tool or check |
Current use |
Remaining boundary |
|---|---|---|
clang-format |
Mandatory owned C++ formatting in |
Formatting alone checks no behavior |
Clang compiler diagnostics |
Local parsing through clang-tidy and header checks |
Full Clang code-generation/link/test qualification remains separate |
clang-tidy / Static Analyzer |
Broad local whole-source profile |
Findings require triage; cross-translation-unit analysis is not configured |
misc-include-cleaner |
Both static targets reject unused includes, retain paired headers and report missing providers |
Other intentional includes require source review; direct-provider cleanup remains advisory |
clangd |
Can use the configured build compilation database in editors |
No clangd executable was available in the recorded local audit; no editor diagnostics claimed |
ASan / LSan |
Existing CPU SLURM merge qualification |
Separate runtime evidence, not part of local static analysis |
UBSan / MSan / TSan |
No result from this local audit |
Separate instrumented builds and runtime/dependency coverage are needed |
Source-based coverage |
No result from this local audit |
Requires instrumented runtime workloads and an explicit coverage scope |
The local audit on 27 October 2026 analyzed all 132 tracked .cpp files and
parsed all 100 owned headers individually with LLVM 18.1.3. The broad scan’s
745 distinct diagnostics are an inventory for triage, not 745 confirmed bugs.
No solver C++ was changed by this tooling work. See the
dated audit record and
run ledger for source identity, resources,
commands, setup failures and limitations. This does not establish full LLVM
runtime or cross-translation-unit qualification.
The workflow follows the official clang-tidy documentation. Cross-translation-unit analysis, clangd compile commands, UBSan and source-based coverage are separate LLVM facilities; listing them does not claim they have been run.