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.