The problem is that in the "arguments" field of the compile_commands.json entry, "-I /home/user/project/include" is a single argument. (So is "-o example" though that doesn't cause any harm in this case.)
That means the command that's executed will be as if you had written:
$ /usr/bin/clang++ "-I /home/user/project/include" "-o example" /home/user/project/common/example.cc
(note the quotes around "-I /home/user/project/include" and "-o example").
If you run that command, that gives the same 'common/example.hh' file not found clang(pp_file_not_found) error. Basically, the quotes make clang think that the argument to -I starts with a space character, which then does not resolve as a path.
The solution is to place each token of the command line in a separate element of "arguments":
[
{
"directory": "/home/user/project",
"file": "/home/user/project/common/example.cc",
"arguments": [
"/usr/bin/clang++",
"-I",
"/home/user/project/include",
"-o",
"example",
"/home/user/project/common/example.cc"
],
"output": "example"
}
]
The reason "arguments" is interpreted in this way is that the format specification for compile_commands.json describes "arguments" as follows:
arguments: The compile command argv as list of strings. This should run the compilation step for the translation unit file. arguments[0] should be the executable name, such as clang++. Arguments should not be escaped, but ready to pass to execvp().
Basically, "arguments" are expected to be the command line arguments as a shell would parse them and pass them to the argv of a program. Since arguments are space-separated, the only way a single argument can contain an internal space is if that argument was quoted on the command line.
Finally, I'll note that compile_commands.json files are generally not meant to be written by hand, but instead generated by a tool (usually your project's build system or something related). See https://clangd.llvm.org/installation#project-setup for a list of commonly used tools to generate them.
If you must hand-write a compile_commands.json, it's probably easier to use "command" instead of "arguments" (refer to the spec for an example), precisely to avoid issues like this.