Make clangd 20 Useful in a C++ Project
Your editor squiggles a perfectly good header, and clangd 20 is usually the culprit: it is parsing your file without your project's real flags. This guide gets it checking a C++ file with the right compilation flags, gives you a repeatable way to diagnose missing project configuration, and adds a few settings that sharpen editor feedback without making the whole machine noisy.
The route
Jump straight to the step you need, or tick off Done means at the end.
The examples use Ubuntu clangd version 20.1.8, from package clangd-20 version 1:20.1.8~++20250804090239+87f0227cb601-1~exp1~20250804210352.139. Allow about twenty minutes.
- You need a shell, clangd 20 and a C++ project.
- Why the command line? An editor plugin normally starts clangd for you, so the checks here use the shell. clangd is not a standalone compiler.
- What it touches: the examples read project files and write no configuration until you choose to add it.
1. Confirm the installed binary
Start with a read-only version check. It needs no elevated privileges:
$ command -v clangd-20
/usr/bin/clangd-20
$ clangd-20 --version
Ubuntu clangd version 20.1.8 (++20250804090239+87f0227cb601-1~exp1~20250804210352.139)
Features: linux+grpc
Platform: x86_64-pc-linux-gnu
The executable is a language server. An editor plugin speaks the Language Server Protocol to it over standard input and output. Run clangd-20 in a terminal with no LSP client and you get no interactive source checker. Use --check for a direct, one-file diagnostic.
Checkpoint
If command -v finds another clangd first, either use that one deliberately or call /usr/bin/clangd-20 in your editor's configuration. Do not debug one binary while the editor launches another.
2. Check one file without an editor session
Make a harmless test file in a temporary directory, or substitute a real source path. The --check option parses one file in isolation and does not act as a language server:
$ clangd-20 --check=/path/to/project/src/main.cc 2>clangd-check.log
$ grep -E 'Testing on source file|All checks completed|error' clangd-check.log
I[...] Testing on source file /path/to/project/src/main.cc
I[...] All checks completed, 0 errors
- Success looks like
All checks completed, 0 errorsand a zero exit status. Timestamps and process details vary. - The log is on standard error, which is why the example redirects it.
- A missing compilation database is not fatal. clangd can fall back to a generic command. That is fine for a smoke test, but it does not prove your real include paths and language flags are in play.
Remove the temporary log when you have finished reading it. There is no project state to undo here; clangd only read the source and any configuration it found.
3. Give clangd the compilation database
For reliable C++ results, make compile_commands.json available. clangd looks for it in the current directory and in parent directories of the source file. You can also point it at a directory explicitly:
$ clangd-20 --compile-commands-dir=/path/to/project/build \
--check=/path/to/project/src/main.cc 2>clangd-check.log
$ grep -E 'compile command|All checks completed|error' clangd-check.log
I[...] Compile command from CDB is: [...] /path/to/project/src/main.cc
I[...] All checks completed, 0 errors
Replace both placeholders with real paths. The option wants the directory containing the database, not the JSON file itself. If the normal directory search cannot find the build tree, put this option in your editor plugin's server arguments.
Your build system generates the database. Configure the project the way its build documentation says, then check the file exists:
$ test -r /path/to/project/build/compile_commands.json && \
echo 'compilation database is readable'
compilation database is readable
Tip
Do not hand-edit a generated database as your first response to diagnostics. Fix the build configuration that produced it, regenerate, and rerun the check. That keeps the editor view aligned with the command that builds the program.
4. Add project settings only when needed
clangd 20 reads a project .clangd YAML file when configuration is enabled. It also reads a user config.yaml under $XDG_CONFIG_HOME, normally ~/.config/clangd/config.yaml on Linux.
- The project file travels with the repository.
- The user file affects all your projects, so it is a poor home for a repository-specific include path.
Use a minimal project file for settings that belong to the project, such as enabling clang-tidy:
Diagnostics:
ClangTidy:
Add: [readability-*, performance-*]
This is a configuration fragment, not a complete build description. Commit the file only if the project agrees on that policy, and check any extra keys against the clangd documentation. If a new setting makes diagnostics unusable, remove it or restore the previous committed version. Do not use sudo to edit a project file.
Tip
To test whether configuration is the problem, run the same --check command from the project, then again from a clean copy or with the project file temporarily moved aside. Save the original first if it is not tracked. This is reversible, but do not delete a configuration file you may need to recover.
5. Tune the common editor annoyances
Most projects do not need every clangd flag. Three options have clear boundaries:
- Background indexing.
--background-indexasks clangd to index project code in the background and persist the index. It can use noticeable disk, CPU and memory, so enable it when cross-file navigation is worth that cost. - clang-tidy.
--clang-tidyenables clang-tidy diagnostics. Use the project's agreed checks where possible; extra warnings can bury compiler errors. - Formatting fallback.
--fallback-style=nonechooses the clang-format style used when no.clang-formatfile exists. It is handy when you do not want clangd inventing formatting, but it does not replace the project's real format file.
Set these in the editor's clangd arguments rather than exporting a global setting for every project. clangd also accepts flags in CLANGD_FLAGS, but that environment variable is easy to forget and can make two editor sessions behave differently.
Tip
For a slow or noisy session, use a bounded worker count such as -j 2, or use --log=info while investigating. --log=verbose suits a short diagnostic capture, not a permanent default. Logs can contain file paths and build details, so treat them as project information before sharing.
6. Allow a compiler driver only if trusted
The --query-driver option gives clangd a comma-separated list of globs for GCC-compatible drivers that it may execute to discover system include paths.
Security warning
This is a security-sensitive boundary. Only allow compiler drivers that you trust and expect on the machine.
$ clangd-20 --query-driver=/usr/bin/g++-*,/usr/bin/clang++-* \
--check=/path/to/project/src/main.cc 2>clangd-check.log
Do not copy a broad wildcard from an untrusted project, and do not allow a driver path inside a writable source tree just to silence an include warning. With a custom toolchain, establish who owns that executable, what it runs and how it is updated before adding its path. Removing the option from the editor arguments is the undo step.
7. Read failures in the right order
When completion or diagnostics look wrong, work down the layers in this order:
- Which binary? Confirm the editor launched
clangd-20, not a different version. - Run the check. Run
--checkon the affected file and capture standard error. - Find the command. Look for a compilation command from the database. A generic fallback means the project flags were not found.
- Inspect the entry. Check the source file's path, language mode, include directories and defines in the compilation database.
- Only then investigate clangd options, clang-tidy checks or the editor plugin.
Warning
Do not solve a missing header by adding random system directories to a user-wide config. That can hide a broken build and make another project parse incorrectly. Fix the compilation database or the build command, then rerun the same verification.
Done means
- Right binary.
clangd-20 --versionreports the installed 20.1.8 binary you intended to use. - Clean check.
--checkcompletes with zero errors on a representative source file. - Real flags. The check uses the project's compilation database, or you have recorded why the fallback is acceptable.
- Small settings. Project settings are minimal, reversible and stored at the correct scope.
- Costs understood. Background indexing, diagnostics and logging are enabled only where their resource and privacy costs are understood.
- Trusted drivers. Any
--query-driverentry names a trusted compiler driver rather than an arbitrary writable path.