Skip to content

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:

clang-include-cleaner --print=changes -p <build-dir> <source.c>

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:

  • Lusoris header only: fork-added, in scope.
  • Netflix header: upstream-mirrored, out of scope (preserves rebase parity per CLAUDE.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

ninja -C build-iwyu              # 70/70 targets relink clean
meson test -C build-iwyu --suite=fast   # 49/49 OK

No score-snapshot regen needed (header-only changes).

6. Follow-ups (out of scope for this PR)

  1. DNN audit inside the container: rebuild dev/Containerfile, then run clang-include-cleaner -p build-iwyu on core/src/dnn/*.c and the four ONNX-Runtime feature extractors (feature_dists.c, feature_lpips.c, feature_mobilesal.c, transnet_v2.c plus fastdvdnet_pre.c). Defer to container-pinned tooling because the host lacks ONNX Runtime.
  2. CUDA / SYCL / HIP / Vulkan backends: same container-based pass for core/src/{cuda,sycl,hip,vulkan}/ and core/src/feature/{cuda,sycl,hip,vulkan}/.
  3. Promote IWYU to a tighter gate: once 1+2 land, consider running clang-include-cleaner --print=changes in CI on the fork-file allowlist with non-zero exit on suggestions. Today make lint runs IWYU but does not gate.
  4. Reusable script: encapsulate the dedup-and-triage workflow under scripts/lint/iwyu_audit.py so future audits skip the bespoke Python one-liners used here.

7. References