Clang provides a user-friendly framework for writing basic static-analysis checks. In this post we will see how analysis on the clang abstract syntax tree (AST) can be used to implement powerful checks for single translation units pretty easily.

The problem

As an example we will write a static analysis check which can catch the following problematic code:

Here B defines a virtual method B::f with a name identical to a member function in its base class A which was never intended to be customized (i.e. it was not declared as virtual there).

This will create the confusing situation that depending on the type the member function was called through different functions will be called, e.g.

We will write a check that catches the problematic method declaration in class B.

Setting up needed tools

We will write our check as an extension of the clang-tidy tool which is part of clang.

If we have a compilation database for our source we can perform a check with clang-tidy by running in the build directory (assuming we have a compilation database)

or on any source file, even without compilation databases, but with no automatic support for custom build flags

Since we will be working directly inside the clang tool sources we need to check out and build the upstream sources.

Make sure you add the bin/ directory of that build to your path, e.g.

The clang-tidy sources are in tools/clang/tools/extra/clang-tidy inside the LLVM source tree.

Let’s change to the clang-tidy source directory and add our check (which we will aptly call VirtualShadowingCheck).

We will add our tool to the [misc] category of clang-tidy checks and before anything else will need to create the needed files and integrate them into the build. Thankfully there is a tool doing all of that for us:

This will create misc/VirtualShadowingCheck.h and misc/VirtualShadowingCheck.cpp, and additionally include it in misc/MiscTidyModule.cpp so it can be run as a normal part of clang-tidy. As of clang-tools-extra svn revision 236309 (git commit 6a5bbb2) we still need to modify misc/CMakeLists.txt so that our newly added dependency VirtualShadowingCheck.cpp comes before the LINK_LIBS line.

We can now create a version of clang-tidy including our checker by rebuilding llvm and tools. To run it on some code we would run

Here we have first disabled all default-enabled checks with -* and then exclusively enabled our check. Right now running this does not output too much interesting information.

Anatomy of a checker

Looking at misc/VirtualShadowingCheck.h we find

Here VirtualShadowingCheck is our custom check defined inside the clang::tidy namespace. It derives from ClangTidyCheck. We will need to provide implementations for two functions, registerMatchers and check (remove their dummy implementations for the time being):

• in registerMatchers we register clang AST matchers to filter out intesting source locations, and
• with check we provide a function which is called by the clang machinery whenever a match was found; we can perform further actions here (e.g. emit a warning).

In our case we want to check for any virtual method of some class whether any of the class’ bases defined a method with the same name, and our implementation strategy will be

• filter out declarations of any virtual method with a matcher registered in registerMatchers, and
• walk the bases of the matched class in check and compare to the matched virtual method.

A matcher for virtual methods

Clang comes with a large set of basic matchers for many use cases. Chaining them allows creating powerful matchers.

To get a feeling for the kind of AST node representing B::f looking at the clang AST is helpful,

We see the two classes defined in lines 6 and 10 as CXXRecordDecl which encode both classes and structs; the two definitions of f in lines 8 and 13 encoded as CXXMethodDecls which encapsulate (not much suprisingly) declarations of methods in C++.

The filter we need would first need to catch method declarations and then then refine that to only methods declared virtual. As first filter we use the cxxMethodDecl matcher,

which finds 4 CXXMethodDecls (two for our explicitly declared methods f and two for implicitly declared methods).

We chain that with the isVirtual matcher to only match virtual methods,

which finds just the method we are interested in.

All left for us to do is to update the definition of VirtualShadowingCheck::registerMatchers in misc/VirtualShadowingCheck.cpp so it matches virtual method declarations,

This binds the identifier method to the found method (note its placement with respect to the parentheses).

Testing the check

If you have built clang-tidy with

you will have seen that the dummy test case added by add_new_check.py now fails so we should update it to at least correctly reflect our intended use case.

Here we have added three test cases:

• the line marked problematic is our initial problem and should trigger a warning. We have specified the location of the expected warning with the CHECK-MESSAGES macro: the warning should be on the next line (+1) on column 3. We specified the full expected warning text.
• the lines marked OK(1) and OK(2) should not trigger the warning since they represent valid use cases; consequentlially we added no CHECK_MESSAGES markup.

We can already add the diagnostic message to VirtualShadowingCheck::check,

If we rerun the test suite we will see that there is still some work left,

Processing of base classes

The matcher we registered will call VirtualShadowingCheck::check whenever a matching AST node was found. There we can retrieve the matches by name with

Here Result.Nodes.getNodeAs<CXXMethodDecl> returns a CXXMethodDecl* which is valid as long as the translation unit is loaded (i.e. much longer than check is running).

We can already stop processing if the class containing method has no bases,

Rerunning the test suite shows that now case OK(1) is removed, but we still match OK(2).

To check the base classes for non-virtual methods with identical names we need to walk the tree of bases; clang provides infrastructure to perform that walk with CXXRecordDecl::forallBases,

BaseMatches is a callback with the signature

and BaseDefinition a CXXRecordDecl pointing to the declaration of a base class. UserData can point to something we could use to pass additional information along1. With AllowShortCircuit = true the callback will be called for all bases as long the callback returns true; for AllowShortCircuit = false all bases would be walked. If the class has no bases CXXRecordDecl::forallBases returns false.

We can use this to recursively walk the tree of bases. We will pass a pointer to method as Userdata to perform checks on the name. Inside VirtualShadowingCheck::check we would call

i.e. call some predicate CandidatePred for all bases of the class containing method and give up if none of them returns true.

If this would not exit check we emit a warning for method since it collides with some base’s name. With this VirtualShadowingCheck::check’s declaration would look like

Now all the work left is to define the predicate.

Rerunning the test suite show that we have covered all our test cases,

Conclusion

While we have just scratched the surface of what is possible, it has never been easier to write custom static analysis checks of C++ code. Clang provides both infrastructure and tools which allow the user to focus on the real problem – formulating the problem.

1. In the development version clang-3.8 the signature changed and UserData got dropped,

bool CXXRecordDecl::forallBases(ForallBasesCallback *BaseMatches, bool AllowShortCircuit = true)


Now ForallBasesCallback is a callable taking a const CXXRecordDecl* as only argument. To pass additional data along one can explicitly (and in a type-safe manner) capture data with a lambda closure.