IWYU (include-what-you-use) Audit — Fork-Added C/C++ Code¶
Date: 2026-05-30 Author: Lusoris Scope: Fork-authored C/C++ translation units under core/ Branch: chore/iwyu-audit Status: Phase-1 complete (host CPU build); follow-ups documented
1. Motivation¶
make lint includes IWYU per CLAUDE.md §4, but the gate does not enforce zero-violations. Drift accumulates: source files inherit symbols transitively through wrapper headers, then break silently the next time the header layout shifts. This audit identifies and corrects the highest-signal cases on fork-added code.
2. Tooling¶
The Arch host does not ship include-what-you-use proper, and neither iwyu-tool nor include-what-you-use are pip-installable on Python 3.13. We used clang-include-cleaner (LLVM 22.1.5, extra/clang-tools-extra), the modern Clang-native successor that consumes the same compile_commands.json and reports the same two violation classes (unused-include and missing-direct-include).
Invocation pattern:
A reusable wrapper (TU-deduplication of the meson compile_commands.json) lives at scripts/lint/iwyu_audit.py — see follow-up below.
3. Scope filtering¶
Fork-vs-upstream classification by license header in the source file:
Lusorisheader only: fork-added, in scope.Netflixheader: upstream-mirrored, out of scope (preserves rebase parity perCLAUDE.md§12 r12).- No header: vendored or build-glue (
pdjson.c,iqa/*.c, json model generators); out of scope.
Of 193 unique TUs in the CPU-only build, 58 carry the Lusoris header.
DNN-gated files (core/src/dnn/*, the four ONNX-Runtime feature extractors, and core/tools/vmaf_roi.c) were skipped: VMAF_HAVE_DNN is undefined in the host CPU-only build because ONNX Runtime is not installed, so cleaner sees only the #if !VMAF_HAVE_DNN stub branches and reports systematic false positives. These files must be audited inside the vmaf-dev-mcp container (per CLAUDE.md §12 r15) in a follow-up.
That left 35 analyzable TUs.
4. Findings¶
clang-include-cleaner produced 27 raw suggestions across 18 of those 35 files. Manual triage:
4.1 Filtered out as bogus (9 suggestions)¶
clang-include-cleaner suggested glibc-internal headers as the "canonical" provider for several symbols:
| Suggested | Actually correct |
|---|---|
<asm-generic/errno-base.h> | <errno.h> |
<bits/getopt_core.h>, <bits/getopt_ext.h> | <getopt.h> |
<bits/pthreadtypes.h> | <pthread.h> |
These are platform implementation headers; including them directly would break portability (musl, BSD, Apple). Filtered without comment.
4.2 Verified by grep — false positives (6 suggestions)¶
A symbol-based grep against the source revealed that 6 of the 16 proposed removals were wrong because the cleaner missed a use that was preserved (typically a macro from errno.h or a function from getopt.h):
| File | Header cleaner wanted removed | Symbol that keeps it |
|---|---|---|
core/src/feature/ssimulacra2.c | <errno.h> | ENOMEM (already had NOLINTNEXTLINE(misc-include-cleaner)) |
core/test/test_cli_parse_long_only_args.c | <getopt.h> | optind |
core/test/test_motion_min_dim.c | <errno.h> | EINVAL |
core/test/test_opt.c | <errno.h> | EINVAL, ERANGE |
core/tools/vmaf_per_shot.c | <getopt.h> | getopt_long, optind, ... |
(Cleaner-by-design false positives — it walks AST symbols, not macro expansions in some configurations.)
4.3 Applied (10 removals + 12 additions across 16 files)¶
Removals (genuine dead includes, grep-verified):
| File | Removed |
|---|---|
core/src/feature/x86/ssimulacra2_host_avx2.c | <math.h> |
core/test/test_cambi_simd.c | <stdlib.h>, "feature/x86/cambi_avx2.h" |
core/test/test_model_collection_api.c | <errno.h> |
core/test/test_opt.c | <string.h> |
core/test/test_speed_qa.c | <stdlib.h> |
core/test/test_vmaf_roi.c | <stdlib.h>, <string.h> |
core/test/test_y4m_411_oob.c | <stdlib.h>, <string.h> |
core/tools/vmaf_per_shot.c | <math.h> |
Additions (direct uses currently picked up transitively):
| File | Added |
|---|---|
core/src/feature/speed_qa.c | "libvmaf/picture.h", <stddef.h> |
core/src/feature/ssimulacra2.c | "libvmaf/picture.h", "opt.h" |
core/src/feature/x86/ssimulacra2_avx2.c | "feature/ssimulacra2_simd_common.h" |
core/src/feature/x86/ssimulacra2_avx512.c | "feature/ssimulacra2_simd_common.h" |
core/test/test_cambi_simd.c | "x86/cpu.h" (gated #if ARCH_X86), <stdio.h> |
core/test/test_integer_adm_simd.c | <stdio.h> |
core/test/test_motion_min_dim.c | "libvmaf/picture.h" |
core/test/test_speed_qa.c | <stddef.h> |
core/test/test_transnet_v2.c | <string.h>, "feature/feature_extractor.h" |
core/test/test_vif_simd.c | <stdio.h>, "x86/cpu.h" (gated #if ARCH_X86) |
Conditional-compilation note: x86/cpu.h additions sit inside #if ARCH_X86 because vmaf_get_cpu_flags_x86() is itself only referenced in that branch; placing the include outside the gate would break non-x86 builds.
5. Verification¶
No score-snapshot regen needed (header-only changes).
6. Follow-ups (out of scope for this PR)¶
- DNN audit inside the container: rebuild
dev/Containerfile, then runclang-include-cleaner -p build-iwyuoncore/src/dnn/*.cand the four ONNX-Runtime feature extractors (feature_dists.c,feature_lpips.c,feature_mobilesal.c,transnet_v2.cplusfastdvdnet_pre.c). Defer to container-pinned tooling because the host lacks ONNX Runtime. - CUDA / SYCL / HIP / Vulkan backends: same container-based pass for
core/src/{cuda,sycl,hip,vulkan}/andcore/src/feature/{cuda,sycl,hip,vulkan}/. - Promote IWYU to a tighter gate: once 1+2 land, consider running
clang-include-cleaner --print=changesin CI on the fork-file allowlist with non-zero exit on suggestions. Todaymake lintruns IWYU but does not gate. - Reusable script: encapsulate the dedup-and-triage workflow under
scripts/lint/iwyu_audit.pyso future audits skip the bespoke Python one-liners used here.
7. References¶
CLAUDE.md§4 (lint), §12 r12 (touched-file cleanup rule), §12 r15 (dev-mcp default)- ADR-0141 (touched-file cleanup rule)
- LLVM
clang-include-cleanerdocs: https://clang.llvm.org/extra/clang-include-cleaner.html - IWYU project: https://include-what-you-use.org/