Add Missing C++ Headers Safely with clang-include-fixer-20
You will use clang-include-fixer-20 to find a header for an unresolved C++ symbol, apply the suggested include to a source file, and check the result without handing the tool more authority than it needs. Allow 15 to 30 minutes for a project that already has its databases; creating an index for a large project takes longer.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide uses Ubuntu LLVM 20.1.8, installed by the clang-tools-20 package. The installed manual describes three database formats, source-file and standard-input modes, direct symbol queries, and JSON output. The tool is not a header search that can work reliably from a filename alone: it needs project compilation information and a symbol index for normal use.
1. Check the installed tool
Run these commands as your ordinary user. They do not edit source files or require elevated privileges:
$ command -v clang-include-fixer-20
/usr/bin/clang-include-fixer-20
$ clang-include-fixer-20 --version
Ubuntu LLVM version 20.1.8
Optimized build.
$ clang-include-fixer-20 --help
The local command accepts a source path after its options. Keep the -20 suffix in scripts when you want this exact installed major version rather than whichever unversioned LLVM tool happens to be first in PATH.
Checkpoint
--version prints 20.1.8 and --help lists --db, -p, --query-symbol, --stdin, --output-headers and --insert-header.
2. Put the two project databases in place
Normal source-file fixing needs a compilation database, usually compile_commands.json, and a symbol database. The LLVM documentation recommends placing or linking both where the source tree can find them. The symbol database is commonly produced by the companion find-all-symbols tooling and is used to map an identifier to a header.
First inspect what your project already has:
$ find /path/to/project -maxdepth 3 \( -name compile_commands.json -o -name find_all_symbols_db.yaml \) -print
Replace /path/to/project with the real project directory. Do not create an empty database as a shortcut. A missing or unrelated database gives you missing results, misleading matches, or a diagnostic rather than a useful include.
If your build directory is elsewhere, the installed program has -p <string> for the build path. Pass the directory containing the compilation database:
$ clang-include-fixer-20 -p /path/to/build /path/to/project/src/example.cc
The command may change the source file, so make a reviewable change first. A clean Git worktree is a useful baseline:
$ git -C /path/to/project status --short
$ git -C /path/to/project diff -- /path/to/project/src/example.cc
Checkpoint
You can identify the compilation database, the symbol index, and the exact source file that will be processed. If any of those is unclear, stop here and fix the project setup before trying edits.
3. Run a normal include-fix pass
Start with the source file that contains the unresolved symbol. Use the database format that matches your index. For the YAML index documented by LLVM, the explicit form is:
$ clang-include-fixer-20 --db=yaml -p /path/to/build /path/to/project/src/example.cc
The tool analyses the file using its compilation options, looks up a missing identifier, and can insert a header. It may also add namespace qualifiers when that is needed to resolve the symbol. The upstream documentation says that one include is inserted per pass, so repeat the command only after reviewing each change rather than assuming that one invocation fixes every diagnostic.
Use a project copy or a disposable branch while learning the result. This is an editing command, not a read-only report. Do not run it with sudo: root ownership can leave your source or generated files difficult to edit later.
Immediately inspect the diff:
$ git -C /path/to/project diff -- src/example.cc
$ git -C /path/to/project diff --check
Look for an include that really declares the symbol, sensible include ordering, and qualifiers that preserve the intended API. Then build or run the project's normal compile command. An exit status from the fixer does not prove that the program now builds correctly.
4. Query a symbol without parsing a source file
For a quick database lookup, use --query-symbol. Supply the spelling as the database knows it, including a namespace when appropriate:
$ clang-include-fixer-20 --db=yaml -p /path/to/build --query-symbol='std::vector'
This mode is useful when an editor has the symbol under the cursor or when you are checking whether an index contains a candidate. It is not a substitute for compiling the target file: it does not validate the file's macros, language standard, include paths, or overload context.
To inspect symbol and header information as machine-readable output, use --output-headers. The manual shows JSON containing FilePath, QuerySymbolInfos, and HeaderInfos. Capture output in a temporary file if another program will consume it:
$ clang-include-fixer-20 --db=yaml -p /path/to/build --output-headers /path/to/project/src/example.cc > /tmp/include-fixer-headers.json
$ head -n 12 /tmp/include-fixer-headers.json
Do not treat every returned header as automatically safe. A symbol can have more than one relevant header, and the best choice depends on the project's public interface and include policy.
5. Handle unsaved editor content deliberately
--stdin tells the tool to overlay the source file's content with standard input while retaining that source file's compilation options. It is intended for editor integration. The source path still matters because it anchors the compilation command and database lookup.
$ cat /path/to/project/src/example.cc | clang-include-fixer-20 --db=yaml -p /path/to/build --stdin /path/to/project/src/example.cc
Do not assume that this pipeline writes an edited file back to disk. Standard-input workflows and the structured --insert-header option are designed for an integration to exchange data with the editor; capture and inspect the tool's output according to that integration's protocol. If you need a durable change, save the buffer separately, review the diff, and run the normal source-file pass.
--insert-header=<string> accepts the YAML or JSON request format shown by the local manual and writes its result to standard output. It is an editor protocol, not a convenient replacement for the normal command. Keep its payload generated by the editor, because hand-editing offsets and ranges is an easy way to insert a header at the wrong location.
6. Recover from a bad suggestion
If the diff is wrong, undo it before doing another pass. With Git, revert only the file you inspected:
$ git -C /path/to/project restore --source=HEAD -- src/example.cc
$ git -C /path/to/project status --short
Warning
That restore discards uncommitted changes in the named file. If it contains work you need, copy or commit that work first, then remove the unwanted include manually. Keep generated or unrelated changes outside the restore command.
Common failures have distinct causes. A missing compilation database means the tool cannot reproduce the file's compiler options. A missing symbol database means it has nothing reliable to map to a header. An unresolved symbol can also be a spelling, namespace, macro, or language-standard problem rather than a missing include. Try --extra-arg-before=<string> or --extra-arg=<string> only when you know the missing compiler option; arbitrary flags can make the analysis less representative of the real build.
Use -q when terminal output is distracting, not when you need to diagnose a failure. Use --minimize-paths only when you have checked that the shortened include paths match the project's include conventions. The manual does not make either option a promise that the resulting include is correct for every build system.
Done means
clang-include-fixer-20 --versionidentified the installed LLVM 20.1.8 binary.- The source file was analysed with the right compilation database and symbol-index format.
- The proposed include and any namespace change were reviewed in a diff.
- The project build or targeted test passed after the edit.
- Any bad change was recoverable through a reviewed Git restore or manual edit.