Biswajit Banerjee

Auto-modernizing C++ code

Using clang-tidy with cmake

The Clang static analyzer tools come with a handy interface called clang-tidy.

I’ve been try to set up this tool in my cmake toolchain with varying levels of success. In particular I’d like to automate the conversion of old code into a version that uses C++11 and some C++14 constructs.


Attempt 1


In my first attempt I created a cmake directory at the top level of my source tree and added a clang-tidy.cmake file that contained the following:


  file(GLOB_RECURSE ALL_MY_SOURCE_FILES *.cpp *.hpp *.cc *.h)
  add_custom_target(
        clang-tidy
        COMMAND /usr/bin/clang-tidy
        ${ALL_MY_SOURCE_FILES}
        -config=''
        -export-fixes='clang-tidy-fixes.dat'
        --
        -std=c++14
        -I${MY_SOURCE_INCLUDE_DIR}
        -I${MY_MPI_INCLUDE_DIR1}
        -I${MY_MPI_INCLUDE_DIR2}
        -I${MY_MPI_INCLUDE_DIR3}
        -I${MY_BOOST_INCLUDE_DIR}
        -I${MY_VTK_INCLUDE_DIR}
  )

At the bottom of my main CMakeList.txt file I added


  include(cmake/clang-tidy.cmake)

When I ran cmake and then make clang-tidy, there were several complaints about header files not being found, but the suggested fixes were written to the file clang-tidy-fixes.dat. That file contained numerous suggested fixes in the format


    - FilePath:        /path/to/file/parser.h
      Offset:          9558
      Length:          52
      ReplacementText: '(const auto & token : tokens)'

Going through that file and then hand-coding the changes looked daunting, so I looked at automating the code change process.


Attempt 2


To make clang-tidy apply the fixes automatically, I replaced


        -export-fixes='clang-tidy-fixes.dat'

in clang-tidy.cmake to


        -fix-errors

When I ran make clang-tidy after that change, fixes were automatically applied, but also applied multiple times. For example, I got


   std::move(std::move(std::move(std::move(a))));

instead of


   std::move(a);


Attempt 3


Clearly, attempt 2 wasn’t solving the problem, and I went back to the clang-tidy manual and found that I could actually export the compile commands from by build using


  cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ../src

That command created a compile_commands.json file which contained all the path and header information that a build could need.

I then located the python script for running clang-tidy from


  /usr/lib/llvm-3.8/share/clang/run-clang-tidy.py

and, from my build directory containing compile_commands.json, I ran


  run-clang-tidy.py ../src -checks=modernize* -fix

No more complaints about missing header files, and all fixes appeared to have been applied only once. It’s much easier to use this process than to try to create a custom target for cmake.

 