clang-tidy

clang-tidy cant detect compile_commands.json


Heres my compile_commands.json :

{ "directory": "/data/data/com.termux/files/home/cpulator", "file": "allocator.cc", "output": "allocator.o", "arguments": ["/data/data/com.termux/files/usr/bin/clang-16", "-xc++", "--sysroot=/data/data/com.termux/files", "events.cc", "-o", "events.o", "--target=arm-altera-eabi", "-nostdlib", "-Wall", "-Wextra", "-Wpedantic", "-c", "-O0", "-gdwarf-4", "-fno-exceptions", "-fno-rtti", "-fno-inline", "--target=armv4t-altera-unknown-eabi"]}

When I run clang-tidy allocator.cc, it acts as if it cant detect the compile_commands.json file.

Error message:

Error while trying to load a compilation database:
Could not auto-detect compilation database from directory "/data/data/com.termux/files/home/cpulator"
No compilation database found in /data/data/com.termux/files/home/cpulator or any parent directory
fixed-compilation-database: Error while opening fixed database: No such file or directory
json-compilation-database: Expected array.

How do I fix this?


Solution

  • Error message meaning

    The error message:

    json-compilation-database: Expected array.
    

    comes from JSONCompilationDatabase.cpp:

    bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
      llvm::yaml::document_iterator I = YAMLStream.begin();
      if (I == YAMLStream.end()) {
        ErrorMessage = "Error while parsing YAML.";
        return false;
      }
      llvm::yaml::Node *Root = I->getRoot();
      if (!Root) {
        ErrorMessage = "Error while parsing YAML.";
        return false;
      }
      auto *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
      if (!Array) {
        ErrorMessage = "Expected array.";        // <-------------------
        return false;
      }
    

    It means the compilation_database.json file was found, but did not begin with the required square bracket character [.

    Fixing compile_commands.json

    The documentation of the compile_commands.json format explains (emphasis mine):

    A compilation database is a JSON file, which consist of an array of “command objects” ...

    To fix your compile_commands.json file, simply insert [ at the start and ] at the end.

    Why are the brackets missing when using -MJ?

    One possible reason you have a compile_commands.json without brackets is you used the clang -MJ option:

    -MJ<arg>

    Write a compilation database entry per input

    Notice it says "entry"; that means the output is just one element of the array that compile_commands.json is supposed to contain. It is therefore incorrect to use it to directly create that file:

    $ clang -MJcompile_commands.json ...      # WRONG!
    

    What you have to do instead is write each file's entry to its own file, then put them all together afterward. For example, in one of my Makefiles I have something like (this is an untested simplified fragment):

    %.o: %.cc
        $(CXX) -c -o $@ $< -MJ$@.json
    

    which creates foo.o.json (and foo.o) for every foo.cc file compiled. Then I have another rule (this one is tested):

    compile_commands.json:
        (echo "["; cat *.o.json; echo "]") > $@
    

    that I just run manually when needed.

    This isn't a particularly good solution due to requiring manual invocation of a rule, it's just meant to illustrate the requirement to assemble a properly-formed compile_commands.json from the fragments produced by -MJ.

    Doing it better with cmake

    cmake 3.5 or later has the CMAKE_EXPORT_COMPILE_COMMANDS variable that will take care of assembling a properly-formatted file without additional intervention.

    (It is not my intention to recommend cmake here, I'm just noting that, for this task, cmake has a good solution.)