include-what-you-use (IWYU)
include-what-you-use tool — fixing redundant and missing includes, CMake integration, IWYU mappings, pragma directives, and CI integration.
TL;DR
include-what-you-use (IWYU) analyzes which #include directives are needed and suggests removing redundant ones and adding missing ones. It reduces compilation times and makes dependencies explicit.
# Install
sudo apt install iwyu
# Run against a file
iwyu -std=c++20 main.cpp -- -I/path/to/headers
# Apply suggestions
iwyu_tool.py -p build/ | fix_includeInstallation
# Ubuntu/Debian
sudo apt install iwyu
# Fedora
sudo dnf install include-what-you-use
# macOS
brew install include-what-you-use
# From source (needs matching LLVM/Clang version)
git clone https://github.com/include-what-you-use/include-what-you-use
cd include-what-you-use && git checkout clang_17
cmake -S . -B build -DCMAKE_PREFIX_PATH=/usr/lib/llvm-17
cmake --build buildBasic Usage
# Analyze a single file with compile commands
iwyu -std=c++20 -I./include main.cpp
# Example output:
# main.cpp should add these lines:
# #include <string> // for std::string
# #include <vector> // for std::vector
#
# main.cpp should remove these lines:
# - #include <iostream> // lines 3-3
#
# The full include-list for main.cpp:
# #include <string>
# #include <vector>CMake Integration
IWYU integrates via CMAKE_CXX_INCLUDE_WHAT_YOU_USE:
# CMakeLists.txt
find_program(IWYU NAMES include-what-you-use iwyu)
if(IWYU)
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE
${IWYU}
-Xiwyu --mapping_file=${CMAKE_SOURCE_DIR}/iwyu.imp
-Xiwyu --no_fwd_decls
)
endif()cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build 2>&1 | grep -A 5 "should add\|should remove"Or use iwyu_tool.py for a cleaner workflow:
# Generate compile_commands.json first
cmake -G Ninja -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
# Run IWYU over all files
iwyu_tool.py -p build/ -- -Xiwyu --mapping_file=iwyu.imp
# Apply the fixes automatically
iwyu_tool.py -p build/ | fix_includes --nosafe_headersIWYU Mapping Files
Standard library headers don't always match the symbols you're using. A mapping file tells IWYU which header provides which symbol:
# iwyu.imp — IWYU mapping file (JSON-ish format)
[
# Use <string> not <basic_string.h>
{ "include": ["<bits/basic_string.h>", "private",
"<string>", "public"] },
# Map Eigen symbols
{ "symbol": ["Eigen::Matrix", "private",
"<Eigen/Core>", "public"] },
# Forward declaration mapping
{ "symbol": ["std::ostream", "private",
"<iosfwd>", "public"] }
]IWYU Pragma Directives
Control IWYU behavior in source files with pragmas:
// Keep this include even if IWYU thinks it's unused
#include <some/header.h> // IWYU pragma: keep
// Export this include — files that include this header
// don't need to include <vector> separately
#include <vector> // IWYU pragma: export
// Associate a forward declaration with a full include
#include <iosfwd> // IWYU pragma: no_include <ostream>
// Tell IWYU a file is an implementation detail
// (in pch.h or aggregate headers)
#include "impl/detail.h" // IWYU pragma: private, include "public_api.h"
// Silence IWYU for a whole file
// IWYU pragma: no_include_detailsForward Declarations
IWYU suggests forward declarations when only a pointer or reference is used:
// widget.h — before IWYU
#include "dialog.h" // only uses Dialog*
// widget.h — after IWYU (Dialog is only used as a pointer)
class Dialog; // forward declaration — no full include neededDisable this behavior if you want full includes everywhere:
iwyu_tool.py -p build/ -- -Xiwyu --no_fwd_declsCI Integration
# .github/workflows/iwyu.yml
name: IWYU Check
on: [push, pull_request]
jobs:
iwyu:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get install -y iwyu ninja-build cmake
- name: Configure
run: cmake -G Ninja -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
- name: Run IWYU
run: |
iwyu_tool.py -p build/ -- -Xiwyu --error_always 2>&1 | tee iwyu.log
# Fail if any "should add/remove" lines
! grep -q "should add\|should remove" iwyu.logCommon Issues and Solutions
False positives from system headers:
# Add the system mapping file
iwyu -Xiwyu --mapping_file=/usr/share/iwyu/gcc.libc.imp ...Too many forward declaration suggestions:
iwyu -Xiwyu --no_fwd_decls ...PCH (precompiled headers) conflict:
# Disable IWYU for targets that use PCH
set_target_properties(mylib PROPERTIES
CXX_INCLUDE_WHAT_YOU_USE "")Boost headers (complex internal structure):
# Use the Boost IWYU mappings
iwyu -Xiwyu --mapping_file=/path/to/boost.imp ...
# Available at: https://github.com/include-what-you-use/include-what-you-use/tree/master/mapfilesBenefits vs Cost
| Benefit | Detail |
|---|---|
| Faster compilation | Fewer headers parsed per TU — can cut 30-50% on large projects |
| Explicit dependencies | Makes what each file actually needs clear |
| Fewer rebuild cascades | Changing impl.h doesn't rebuild files that only needed fwd.h |
| IDE performance | Less work for clangd / IntelliSense |
Cost: Initial fix-up pass can be noisy (hundreds of suggestions on a legacy codebase). Run it incrementally on new files first.