Incremental Static Analysis
published v2.1.0evolving
A large codebase is mostly stones nobody moved today. Texture: scikit-image sample data, CC0.
- Why full static analysis stops fitting into a merge request.
- How a baseline plus a changed-file scope makes it fast.
- Where incremental analysis misses things, and how to cover the gap.
Static analysis is one of the few checks that finds whole classes of defects before code runs. Null dereferences, buffer overruns, leaked resources, unreachable branches. On a small project it runs in seconds and nobody thinks about it.
On a large C codebase it can take hours. At that point it quietly moves out of the merge request and into a nightly job, and its findings arrive the morning after, attached to nobody's change in particular. The tool still works. The feedback loop doesn't.
Most of the code didn't change
A typical merge request touches a handful of files in a codebase of thousands. Analysing everything to judge those few changes is like re-surveying a whole quarry because someone moved three stones.
Incremental analysis keeps a baseline, a full analysis of the main branch, and on each change analyses only what the change could affect. It then reports what is new relative to the baseline, which is the only question a reviewer actually has.
Capture the build once
C analysers need to know how each file is compiled: include paths, defines, flags. The usual way to hand that over is a compilation database, a JSON file listing the exact compiler command for every translation unit [2]. Tools like Bear produce one by watching a normal build [1].
# record how every file is compiled
bear -- make # writes compile_commands.json
# on a merge request: which sources changed?
git diff --name-only origin/main...HEAD -- '*.c' '*.h'
The two inputs an incremental run needs: how files are built, and which ones changed.
Where it misses things
Incremental analysis trades completeness for speed, and the trade has edges. Change a header and every file that includes it is affected, so a naive "changed files only" scope under-analyses. Some checks follow data across files, and those need the dependency graph, not just the diff.
The fix is to keep both loops. Fast incremental runs in every merge request, for feedback while the change is fresh. A full run on the main branch, nightly, which also refreshes the baseline the incremental runs compare against.
Fast feedback on every change, full truth every night.
- Report what's new against a baseline, not everything.
- Header changes widen the scope; plan for them.
- Keep a nightly full run to catch what increments miss.
References
- L. Rizsotto, “Bear: a tool that generates a compilation database for clang tooling.” [Online]. Available: github.com/rizsotto/Bear
- LLVM Project, “JSON Compilation Database Format Specification,” Clang documentation. [Online]. Available: clang.llvm.org/docs/JSONCompilationDatabase.html
Ire o? (Blessings (good fortune to you), Yoruba),
Olarotimi
Revisions
Major versions are rewrites, minor versions add or clarify, patches fix.
- 2.1.0Build step 3c: articles, article layout and the log
- 2.1.0Build step 2: content system (publish by adding a Markdown file)
Every change is recorded on the site log.