C++ Toolchain and Warnings¶
Purpose¶
Document the concrete compiler warning set enforced in the TYPECHECK stage and
the no-standing-suppression rule that governs it. This page describes the
set; it does not choose it. The set is decided and tested in the language
registry (src/vergil_tooling/lib/languages.py), and this page mirrors that
single source of truth.
TYPECHECK Is "The Compiler"¶
For C++, the TYPECHECK stage is the warnings build: a full compile under
each compiler and version with the curated warning set promoted to errors. There
is no separate type checker the way Python has mypy or Rust has cargo check —
the compiler itself, run strict, is the type-and-soundness gate.
The warning flags are threaded into the build as a single CMAKE_CXX_FLAGS
cache value, so the whole set lands on every translation unit's compile line.
The Curated Warning Set¶
The floor is -Wall -Wextra -Werror -Wpedantic, tuned upward with a curated set
of extras. The complete set enforced today:
# Floor
-Wall
-Wextra
-Werror
-Wpedantic
# Curated extras
-Wshadow
-Wconversion
-Wsign-conversion
-Wcast-qual
-Wold-style-cast
-Wnon-virtual-dtor
-Woverloaded-virtual
-Wdouble-promotion
-Wformat=2
-Wimplicit-fallthrough
-Wnull-dereference
What each extra buys:
| Flag | Catches |
|---|---|
-Wshadow |
a local name silently shadowing an outer one |
-Wconversion |
implicit conversions that may change a value |
-Wsign-conversion |
implicit signed/unsigned conversions |
-Wcast-qual |
a cast that drops a const/volatile qualifier |
-Wold-style-cast |
C-style casts in C++ code (use named casts) |
-Wnon-virtual-dtor |
a base class with virtuals but a non-virtual destructor |
-Woverloaded-virtual |
a derived member hiding, not overriding, a virtual |
-Wdouble-promotion |
an implicit float-to-double promotion |
-Wformat=2 |
format-string and security issues (printf-family) |
-Wimplicit-fallthrough |
a switch case falling through without annotation |
-Wnull-dereference |
a path that dereferences a known-null pointer |
One Portable Set, On Purpose¶
This is a single portable set that both current GCC and Clang accept — it is not a per-compiler list. The reasoning is a direct consequence of how the warnings build runs:
- TYPECHECK is a per-compiler×version stage, but the registry command is
version-agnostic — the same argument list runs inside every
clang-*andgcc-*image. The two-diagnostics win comes from the two compilers interpreting these flags, not from two different flag lists. - Under
-Werror, a flag that only one compiler recognizes is itself a hard error. GCC rejects an unknown-Woutright, and Clang's-Werrorpromotes-Wunknown-warning-option. So every flag in the set must be one that both current GCC (>= 13) and Clang (>= 18) accept.
Per-compiler warning sets are deferred. Compiler-exclusive warnings — GCC's
-Wlogical-op and -Wduplicated-cond, for example — are deliberately out of
the current set. Adding them requires a compiler-branched TYPECHECK command,
which means teaching the registry a compiler dimension; that is a separate,
tracked change, not something smuggled into the portable set. When it lands,
this page is updated to describe it.
No Standing Suppression List¶
There is no project-wide standing suppression list for compiler warnings. A
warning is fixed, not muted. The set is promoted to errors precisely so a
warning cannot accumulate as ignored noise — under -Werror it stops the build
until the code is corrected.
If a specific, unavoidable warning genuinely must be silenced, the suppression is local and narrow: a targeted pragma around the smallest possible region, carrying a comment that states the reason. A broad, file-level, or repository- level suppression that hides a whole warning category is not permitted — it defeats the reason the flag is in the set. The discipline mirrors the coverage exclusion rule in Testing and Coverage: gaps are acknowledged narrowly and visibly, never blanketed away.
Relationship to the Static-Analysis Stage¶
The warning set is distinct from the LINT stage, which runs clang-format,
clang-tidy (via run-clang-tidy over the compile_commands.json database),
and cppcheck once on the primary Clang image. TYPECHECK is the compiler's own
judgment under both families; LINT is dedicated static analysis. Both gate a
change; neither substitutes for the other.
cppcheck's curated enable set¶
cppcheck runs with a curated enable set rather than --enable=all:
This deliberately drops three of all's checks:
unusedFunction— an unreliable check for GoogleTest code.cppcheckanalyses one translation unit at a time and cannot see the static-registration machinery behind theTEST()macro, so it reports every test function as dead code. The finding is a systematic false positive, not a real one.information/missingInclude— diagnostics aboutcppcheck's own analysis coverage, not about the code. They add noise without gating quality.
Dropping unusedFunction is tool-tuning of a check that emits systematic
false positives — not a suppression. It honours the
No Standing Suppression List rule exactly: no
real finding is silenced, and no standing suppression-list entry is added. The
distinction is deliberate. A suppression mutes a true warning about the code;
here the check itself is wrong for this codebase's idioms, so the correct fix is
to stop running the broken check, not to catalogue the findings it fabricates.
The narrow, local suppression escape hatch (--inline-suppr plus the packaged
cppcheck-suppressions.txt) still exists for a genuine, unavoidable finding.
cppcheck also excludes the CMake build tree with -i build -i build-sanitize.
Left to walk . unfiltered it descends into the compiler-probe file CMake
generates under build/ (CMakeCXXCompilerId.cpp) and trips toomanyconfigs —
a build artifact, never source. This mirrors the build-dir prune the
clang-format find driver already applies.