Clang Tutorial: The AST Matcher
Table of Contents
In previous post we have learned about Clang abstract syntax tree (AST) and made a simple Clang tool that will print all the
declarations in a given source file. Now Let's play with Clang AST matchers and make another tool to
detect when a std::vector is passed by value - which usually causes a performance hit.
1 Finding std::vector Which Are Passed by Value
The example.cpp:
#include <vector> using namespace std; void foo(vector<int> is); void bar(const vector<int> is); void foobar(const vector<int>& cis); void fooboo(vector<int>& cis);
Let's feed it to our find-vec program:
$ ./find-vec example.cpp -- -std=c++11 example.cpp:5:6 example.cpp:6:6
Here we told Clang parser to use C++ 11 syntax by passing -std=c++11 after --.
2 The Clang AST
If you still remember the Decl and Stmt,
these are the AST nodes that we will come across when processing a source file.
With different Decl, Stmt and their derived classes like NamedDecl and CallExpr,
we can obtain lots of information like variable type, name, definition information, etc.
To examine all the function declarations with any parameter of type std::vector,
we will need to match specific AST node that is:
- Function declaration or definition
- Has at least one parameter of type
std::vector
Clang provides us a domain specific language (DSL) to create predicates on Clang's AST. It is written in and can be used from C++, allowing us to match AST nodes and extract its attributes.
3 The AST Matcher
To use the AST matchers, we need to do is to call a bunch of matcher creation function, chain them together to compose the matcher we need, and/or bind the target node with a name so we can extract it later.
To match a function declaration:
DeclarationMatcher Matcher = functionDecl();
Then let's match its parameter:
DeclarationMatcher Matcher = functionDecl(hasAnyParameter(...));
To matcher a parameter, we can use hasParameter(N, ParamMatcher), which will match
the N'th of parameter with given parameter matcher. Here we will need to match any
parameter that of type std::vector, so we will use hasAnyParameter.
Next match the parameter type:
DeclarationMatcher Matcher = functionDecl( hasAnyParameter(hasType(recordDecl(matchesName("std::vector"))));
4 The MatchCallback
When the AST matcher find the right node, the corresponding
clang::ast_matchers::MatchFinder::MatchCallback will be invoked with matched result.
By providing a MatchCallback,
we can print the function declarations/definitions that accept any parameter of type std::vector that is passed by value.
class VecCallback : public clang::ast_matchers::MatchFinder::MatchCallback { public: virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result) final { llvm::outs() << "."; if (const auto *F = Result.Nodes.getDeclAs<clang::FunctionDecl>(FunctionID)) { const auto& SM = *Result.SourceManager; const auto& Loc = F->getLocation(); llvm::outs() << SM.getFilename(Loc) << ":" << SM.getSpellingLineNumber(Loc) << ":" << SM.getSpellingColumnNumber(Loc) << "\n"; } } };
5 References
Clang also provides a simple document of AST matchers: Matching the Clang AST. For a complete reference of AST matchers, you can find it in AST Matcher Reference.
All the source code in this post can be found at clang-playground.