As a developer, you probably know that parsing C++ source code is very complicated. This is also the reason why there might be fewer good tools for coding assistance than we have in other “lighter” languages. Fortunately, parsing C++ source code has become much more straightforward over the past decade, mainly because of the Clang/LLVM infrastructure. Clang not only introduced a new excellent C++ compiler but also many extra tools that allow us to analyse C++ code efficiently.
Today we’ll meet one tool that can make our life much easier! It’s called clang-tidy.
We’ll cover this valuable utility as it’s also an important part for the Visual Assist internal code analysis system.
Table of Contents
Let’s Meet clang-tidy
Here’s a concise and a brief description of this handy tool:
clang-tidy is a clang-based C++ “linter” tool. Its purpose is to provide an extensible framework for diagnosing and fixing typical programming errors, like style violations, interface misuse, or bugs that can be deduced via static analysis. clang-tidy is modular and provides a convenient interface for writing new checks.
As you can read above, this tool gives us a way to check our source code and fix common issues.
Let’s now see how you can install and work with this tool on Windows.
Installation on Windows
clang-tidy is attached with the main Clang package for Windows. You can download it from this site:
LLVM Download Page – version 11.0 (Nov 2020)
(Or also see other Releases: Download LLVM releases)
When Clang is installed, you can open your command line (for example, for this article I’m using PowerShell) and type:
clang-tidy --version
I get the following output:
PS D:\code> clang-tidy --version
LLVM (http://llvm.org/):
LLVM version 11.0.0
Optimised build.
Default target: x86_64-pc-windows-msvc
Host CPU: haswell
In general, clang-tidy operates on a single input file. You pass a filename, plus standard compilations flags and then extra flags about selected checks that will be performed.
Let’s run a simple test to see how it works.
Running a Simple Test
See this artificial code:
#include <vector> #include <memory> #include <iostream> #include <string> int main() { int a; std::string hello {"hello"}; std::string world {"world"}; if (hello + world == "") std::cout << "empty!\n"; std::unique_ptr<std::string> ptr { new std::string { "abc" } }; ptr.reset(new std::string{ "xyz" }); ptr.reset(NULL); std::vector<int> vec{ 1, 2, 3, 4 }; for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) std::cout << *it << '\n'; }
Can you now play a game and list all of the “spurious” smells in the code? What would you improve here?
Let’s now compare your results with suggestions from clang-tidy.
I ran the tool with the following options:
clang-tidy --checks='modernize*, readability*' hello_tidy.cpp -- -std=c++14
On my machine, I get the those results:
As you can see above, I run it against all checks in the “modernise” and “readability” categories. You can find all available checks here: clang-tidy – Clang-Tidy Checks — Extra Clang Tools 12 documentation
You can also list all available checks for your version with the following command:
clang-tidy --list-checks
In summary the tool found the following issues:
- suggestion to use trailing return type for main()
- suggestion about using .empty() rather than comparing string with an empty string
- make_unique
- nullptr rather than NULL
- modernising range based for loop
- braces around single-line if and loop statements
Experimenting with Fixes
But there’s more!
clang-tidy can not only find issues but also fix them! A lot of checks have “auto-fixes”! By providing the -fix
command, you can ask the tool to modify the code. For example:
clang-tidy --checks='modernize-* readability-container*' -fix hello_tidy.cpp -- -std=c++14
As you can see, this time I used “readability-container” only as I didn’t want to modify braces around simple if statements.
I got the following output:
clang-tidy nicely lists all the fixes that it managed to apply.
The final source code looks as follows:
#include <memory> // oops! #include <vector> #include <memory> #include <iostream> #include <string> auto main() -> int { int a; std::string hello {"hello"}; std::string world {"world"}; if (hello + world.empty()) // oops! std::cout << "empty!\n"; std::unique_ptr<std::string> ptr { new std::string { "abc" } }; ptr = std::make_unique<std::string>( "xyz" ); ptr.reset(nullptr); std::vector<int> vec{ 1, 2, 3, 4 }; for (int & it : vec) std::cout << it << '\n'; }
In the above code sample the tool managed to fix several issues, and we can decide if we want to apply all or maybe just select a few of them. For example, I’m not sure about using a trailing return type for all functions. Additionally, the tool couldn’t improve and apply make_unique
in the place where we declare and initialise ptr
. So hopefully, with each new revision, we’ll get even better results.
But also it’s important to remember that you have to be careful with the fixes.
See lines 1 and 12.
Clang-tidy added extra and duplicated header file “memory” (this was probably needed for make_unique()). This is not critical, but shouldn’t happen. But what’s worse is that at line 12 the code is now wrong.
The line if (hello + world == "")
was changed into if (hello + world.empty())
. This changes the meaning of the statement!
When applying fixes, be sure to review code and check if this is what you expected.
I hope you now get a general idea of how to use the tool and what its output is.
Running this tool from a command line might not be the best choice. Fortunately, there are many options on how we can plug it inside our build system or IDE.
For example, you can learn how to make use of it with CMake on a KDAB blog or… Clang-Tidy, part 1: Modernise your source code using C++11/C++14 – KDAB
Let’s now have a brief look at the options in Visual Studio.
Clang Tools in Visual Studio
clang-tidy support is available starting with Visual Studio 2019 version 16.4.
clang-tidy is the default analysis tool when using the LLVM/clang-cl toolset, but you can also enable it with MSVC compiler:
And you can configure it with a separate property panel:
And when you run a code analysis you can get results to your Error/Warning list:
Unfortunately, the current version of Visual Studio doesn’t offer a way to apply fixes, so you need to modify the code on your own.
Luckily, with the help of Visual Assist, you can change it. See below.
How Visual Assist Makes Things Much Safer And Easier
Visual Assist offers a service called “Code Inspection”, which is based on a standalone LLVM/Clang embedded into the VA system. You can enable it (even in Visual studio 2008!), and for our simple example you might get the following results (in a separate VA Code Inspection Window):
And what’s best is that for many of them you can apply a fix!
See the following context menu:
This is great! Thanks to the embedded LLVM/Clang subsystem, Visual Assist can perform the analysis and help you with many tasks related to code modernisation and fundamental code analysis!
But what’s best is that Visual Assist cleans up the output from clang-tidy and makes sure the fixes are correct and safe. Here’s the code after I applied all suggestions:
#include <vector> #include <memory> #include <iostream> #include <string> int main() { int a; std::string hello{ "hello" }; std::string world{ "world" }; if ((hello + world).empty()) std::cout << "empty!\n"; std::unique_ptr<std::string> ptr{ new std::string { "abc" } }; ptr = std::make_unique<std::string>( "xyz" ); ptr.reset(nullptr); std::vector<int> vec{ 1, 2, 3, 4 }; for (int & it : vec) std::cout << it << '\n'; }
Nice!
As you can see there’s no extra include statement. And what’s most important is in line 12. VA added extra brackets, so the whole expression is now correct and safe!
Summary
In this article, we covered clang-tidy – a handy tool for code analysis that can (experimentally) fix your code automatically! The tool is quite verbose and might be hard to configure to work with large projects. In addition, please make sure you review code when applying fixes.
By default, you can download it and launch from a command line, but it’s much better to use it from Visual Studio (limited).
To get the best experience and safety have a look at the embedded clang-tidy inside Visual Assist – in the form of “VA Code Inspections”. This extra feature makes sure the results of code analysis are easy to read and meaningful, and the fixes are correct.
Today we only scratched the surface of this exciting topic. In two upcoming blog posts you’ll see some more use cases where Visual Assist can help you with code refactoring and modernisation (also leveraging embedded clang-tidy). Stay tuned.
For now you can read more in:
- Clang-Tidy — Extra Clang Tools 12 documentation
- Introduction to Code Inspection Visual Assist Documentation
- Code analysis with clang-tidy in Visual Studio | C++ Team Blog