Skip to content
C++
Analysis & Formatting
Updated 2025-01-01T00:00:00.000Z

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.

bash
# 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_include

Installation

bash
# 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 build

Basic Usage

bash
# 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:

cmake
# 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()
bash
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:

bash
# 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_headers

IWYU Mapping Files

Standard library headers don't always match the symbols you're using. A mapping file tells IWYU which header provides which symbol:

ini
# 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:

cpp
// 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_details

Forward Declarations

IWYU suggests forward declarations when only a pointer or reference is used:

cpp
// 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 needed

Disable this behavior if you want full includes everywhere:

bash
iwyu_tool.py -p build/ -- -Xiwyu --no_fwd_decls

CI Integration

yaml
# .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.log

Common Issues and Solutions

False positives from system headers:

bash
# Add the system mapping file
iwyu -Xiwyu --mapping_file=/usr/share/iwyu/gcc.libc.imp ...

Too many forward declaration suggestions:

bash
iwyu -Xiwyu --no_fwd_decls ...

PCH (precompiled headers) conflict:

cmake
# Disable IWYU for targets that use PCH
set_target_properties(mylib PROPERTIES
    CXX_INCLUDE_WHAT_YOU_USE "")

Boost headers (complex internal structure):

bash
# 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/mapfiles

Benefits vs Cost

BenefitDetail
Faster compilationFewer headers parsed per TU — can cut 30-50% on large projects
Explicit dependenciesMakes what each file actually needs clear
Fewer rebuild cascadesChanging impl.h doesn't rebuild files that only needed fwd.h
IDE performanceLess 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.

Edit on GitHubUpdated 2025-01-01T00:00:00.000Z