Extending Clang-Tidy with a Plugin to Add New Checks for CMake

CMake Logo

Many C++ projects have a set of coding standards regarding what functions to use, and CMake is no exception. Clang-tidy is a program which enforces some universally relevant standards to C++ projects. CMake runs clang-tidy on its code as part of its CI process, but clang-tidy alone does not have a way to enforce standards that are specific to CMake’s code base.

Luckily, clang-tidy has supported loadable plugins for several versions, and LLVM and clang have very comprehensive documentation for their internal APIs. Clang-tidy can also automatically apply its suggested fix to the source file in some cases. This means that a developer who is familiar with the internals of clang-tidy can write a plugin to enforce their own code standards on their own projects, complete with automatic correction.

In partnership with the Department of Computer Science at the University at Albany, Kitware has overseen the development of a clang-tidy plugin that will be used to enforce CMake’s code standards. This will allow CMake maintainers to review code more easily, and has also provided a learning opportunity for graduating seniors in the computer science department who are completing their capstone project.

Our new clang-tidy plugin enforces six CMake-specific coding guidelines:

  1. Calls to strlen() or sizeof() on a string literal should be replaced with calls to cmStrLen().
  2. Instances of std::ifstream, std::ofstream, and std::fstream should be replaced with cmsys::ifstream, cmsys::ofstream, and cmsys::fstream respectively.
  3. Functions with a bool parameter should replace it with an enum class parameter.
  4. Headers that have include guards (#ifndef/#define/#endif) should use #pragma once instead.
  5. std::ostringstream should not be used. Instead, strings should be built using cmStrCat().
  6. Strings should not be concatenated together with + or +=. Instead, they should be concatenated using cmStrCat().

The plugin that we’ve developed is already active in CMake’s CI system, actively enforcing checks 1, 2, and 4 (checks 3, 5, and 6 are currently disabled due to a large number of outstanding violations.) The code for the plugin can be found here. The CI system builds the plugin against the clang-tidy headers and then calls clang-tidy with the --load argument pointed to the newly-built .so plugin file.

In addition, we have added a new feature to CMake to allow clang-tidy to export all of its suggested fixes to a directory, which can then be applied to the source code after the build is complete. This will allow mass refactoring to enable new clang-tidy checks, and can also be used by the CI system to automatically generate a patch file containing its suggested fixes.

This project has proved to be a useful tool for C++ code review in CI systems. In addition to improving the quality of the CMake code base, we hope that the knowledge gained from this project will allow us to develop clang-tidy plugins for customers who want to enforce their own standards on their own code.

As an example of what the plugin can do, consider the following code snippet:

void writeFile()
{
  std::ofstream fout("file.txt");
  fout << "Hello world!\n";
}

CMake’s code standards dictate that cmsys::ofstream, CMake’s internal ofstream implementation which handles internationalization of filenames on Windows, be used instead of std::ofstream. The clang-tidy plugin detects this incorrect usage of std::ofstream, issues a warning to the user, and automatically corrects it:

void writeFile()
{
  cmsys::ofstream fout("file.txt");
  fout << "Hello world!\n";
}

Future work will include adding more checks to CMake’s clang-tidy plugin, upstreaming the #pragma once check back to the LLVM project, and adding a new feature to clang-tidy to add a NOLINT line to the source code in the case that it is not able to apply an automatic fix. This new feature will allow checks that have a large number of outstanding violations to be turned on without failing, giving the existing violations a grace period to be fixed while preventing any new ones from being added to the code base.

Recognition

Kitware would like to recognize Joseph Blaauboer, Mikhail Glebov, and Sean Orner for their hard work on this project, and congratulate them on their graduation from the University at Albany (UAlbany). We would also like to acknowledge Pradeep Atrey, Ph.D., professor at UAlbany, for overseeing this computer science capstone project.

“We would like to thank Kitware for sponsoring this project and allowing us to make a meaningful contribution to the CMake codebase.

We also wanted to extend a special thank you to Kyle Edwards; your expertise and dedication to the computer science field are genuinely inspiring, and it has been a privilege to receive your  exceptional mentorship. We will always be grateful for the essential knowledge and skills we developed under your guidance throughout this project.

Thank you again for your support.”

– A message from the graduating seniors from UAlbany

Leave a Reply