Clangd and Compilation Databases

Last update: 2026-03-11 03:34:10 +0800 +0800
Available translations: 日本語 / 中文(简体) / 中文(繁體)

clangd provides C and C++ language-server features by parsing each open file with a virtual compiler command. Completion, diagnostics, navigation, and refactoring are reliable only when that command represents the real build context: working directory, compiler driver, target, language mode, include paths, preprocessor definitions, and generated headers.

Supplying Compilation Commands #

A compilation database is the preferred source of truth. compile_commands.json contains one command and working directory for each translation unit. CMake can emit it for a build tree:

bash
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
clangd --compile-commands-dir=build
json
[
  {
    "directory": "/work/project",
    "file": "src/main.cc",
    "command": "clang++ -Iinclude -std=c++20 -c src/main.cc"
  }
]

Without --compile-commands-dir, clangd searches from the opened file’s directory through its parents for compile_commands.json or compile_flags.txt. compile_flags.txt is a simpler fallback: it supplies one flag per line to every file and cannot represent targets whose flags differ by translation unit.

text
-Iinclude
-std=c++20
-Wall

Generate the database from the authoritative build, then make it discoverable from the workspace. A symlink from the repository root, an editor setting, or --compile-commands-dir are all valid; copying it risks stale commands. For build systems without native support, a tool such as Bear can observe a clean build, but generated commands must still describe the configuration developers open.

Headers, New Files, and Flag Adjustment #

Build systems do not compile headers directly. For a header, clangd first reuses a command from an opened source that includes it; otherwise it selects a filename-similar database entry. That heuristic can select the wrong target in a multi-platform tree. Open an including source first, name headers consistently with their primary source, or use project configuration to make the intended context unambiguous.

Files absent from the database receive a fallback command. The fallback permits parsing but has none of the target-specific include paths, macros, generated headers, or language mode that the build supplies. Treat its diagnostics as provisional and add the source to the build or provide configuration before editing a new target.

.clangd can adjust commands without duplicating the database:

yaml
CompileFlags:
  Add: [-Ithird_party/api, -DMY_EDITOR_BUILD=1]
  Remove: [-Werror]

Use adjustments for editor-only corrections or flags clangd cannot consume, not to reconstruct a build command by hand. Preserve the compiler executable where possible because its location and name affect default standard-library lookup and language selection.

Diagnosing Incorrect Context #

Run a single-file check and inspect the selected command:

bash
clangd --check=src/main.cc --log=verbose

The verbose log identifies the compilation database, working directory, and adjusted command. When headers cannot be found, compare -I, -isystem, sysroot, and generated-header paths against the actual compile invocation. When parsing uses the wrong dialect, verify -x, -std=, Objective-C versus C++, and the compiler driver. When builtins or ABI-dependent types differ, check --target, toolchain path, and SDK flags.

Regenerate the database after changing build options, dependencies, or generated sources. Ensure code generators run before clangd consumes their output, or provide the generated include directory and checked-in interface headers. Starting the LSP server with additional clangd flags is distinct from changing a file’s compile command; use the latter for source semantics.

Comments

Read or comment on GitHub (sign-in required to comment)