Skip to main content

Raymii.org Raymii.org Logo

Quis custodiet ipsos custodes?
Home | About | All pages | Cluster Status | RSS Feed | Gopher

Run one specific clang-tidy check on your entire codebase

Published: 05-04-2021 | Author: Remy van Elst | Text only version of this article


Table of Contents


Recently I did a major refactor on a piece of code that involved thousands of lines of code which were in one way or another related to string handling. All of the code handled char* (C style character pointer arrays) and the concept of const or ownership was literally unknown in that part of the codebase. The refactored code uses std::string's, but due to the legacy nature, a large number of methods returned nullptr's instead of empty strings (like ""). I understand why this was done, but finding all those instances and the fact it only gives a runtime error was a bit of a bummer.

Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.

You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days.

Luckily clang-tidy is here to save the day. In my IDE, CLion, it gives a warning when you return a nullptr. It however does that only in the file you're currently editing, and since we're talking millions of files, I wasn't going to open them by hand. You can run clang-tidy easily on one file, and it's not hard to run it on an entire codebase as well, using the script run-clang-tidy.py, provided in their packages.

This snippet shows you how to run one specific clang-tidy check, in my case, bugprone-string-constructor, on a (cmake and C++) codebase.

Here's the clang-tidy message in CLion:

screenshot

Example code with undefined behaviour

This is an example piece of code demonstrating the behavior:

#include <string>
#include <iostream>

class Example {
public:
    std::string getName() { return nullptr; }
};

int main() {
    Example ex;
    std::cout << "Example: " << ex.getName() << std::endl;
    return 0;
}

If you try to run the above code example, you'll get a runtime error:

terminate called after throwing an instance of 'std::logic_error'
    what():  basic_string::_M_construct null not valid

Opinions on nullptr and std::string differ depending on who you ask, but as of now it's not possible to construct a std::string with a nullptr.

Run clang-tidy on you entire codebase

Make sure you have clang-tidy installed:

apt install clang-tidy

Navigate into your project folder:

cd my/cpp/project

If you haven't already, create a build folder (mkdir build; cd build) and run cmake with an extra flag to create the compilation database for clang-tidy:

cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug 

In the build folder, run run-clang-tidy. It might be a different command (run-clang-tidy.py or run-clang-tidy-VERSIONNUMBER) depending on your distro's packaging preference.

run-clang-tidy -extra-arg=-Wno-unknown-warning-option -checks='-*,bugprone-string-constructor' 2>&1 | tee -a clang-tidy-result

This will take a while, when the command is finished, you can look at the results, or in the file clang-tidy-result. In my case it gave specific filenames and line numbers where it found the undefined behavior.

The -extra-arg was required due to some other compiler extension flag for our code, you can probably omit that.

The -checks='-*' disables all checks, the next ,bugprone-string-constructor enables only the specific string check I want to run. You can add more specific checks, separate them by a comma. An example with just 2 checks enabled:

-checks='-*,bugprone-string-constructor,bugprone-string-integer-assignment'

An up to date list of clang-tidy checks can be found on the LLVM website.

Tags: c++ , clang , clang-tidy , cpp , development , legacy , refactoring , snippets