The static analyzer is an impressive example of what you can do with the clang infrastructure. It is also possible to extend clang with plugins so that you can add your own functionality to clang. The technique is very similar to adding a pass plugin to LLVM.
Let’s explore the functionality with a simple plugin. The LLVM coding standard requires function names to begin with a lowercase letter. However, the coding standard has evolved, and there are many instances in which a function begins with an uppercase letter. A plugin that warns about a violation of the naming rule can help fix this issue, so let’s give it a try.
Because you want to run a user-defined action over the AST, you need to define a subclass of the PluginASTAction class. If you write your own tool using the clang libraries, then you can define subclasses of the ASTFrontendAction class for your actions. The PluginASTAction class is a subclass of the ASTFrontendAction class, with the additional ability to parse command-line options.
The other class you need is a subclass of the ASTConsumer class. An AST consumer is a class using which you can run an action over an AST, regardless of the origin of the AST. Nothing more is needed for our first plugin.
You can create the implementation in the NamingPlugin.cpp file as follows:
- Begin by including the required header files. Besides the mentioned ASTConsumer class, you also need an instance of the compiler and the plugin registry:
include “clang/AST/ASTConsumer.h”
include “clang/Frontend/CompilerInstance.h”
include “clang/Frontend/FrontendPluginRegistry.h”
- Use the clang namespace and put your implementation into an anonymous namespace to avoid name clashes:
using namespace clang;
namespace {
- Next, define your subclass of the ASTConsumer class. Later, you will want to emit warnings in case you detect a violation of the naming rule. To do so, you need a reference to a DiagnosticsEngine instance.
- You’ll need to store a CompilerInstance instance in the class, after which you can ask for a DiagnosticsEngine instance:
class NamingASTConsumer : public ASTConsumer {
CompilerInstance &CI;
public:
NamingASTConsumer(CompilerInstance &CI) : CI(CI) {}
- An ASTConsumer instance has several entry methods. The HandleTopLevelDecl() method fits our purpose. The method is called for each declaration at the top level. This includes more than functions – for example, variables. So, you must use the LLVM RTTI dyn_cast<>() function to determine if the declaration is a function declaration. The HandleTopLevelDecl() method has a declaration group as a parameter, which can contain more than a single declaration. This requires a loop over the declarations. The following code shows the HandleTopLevelDecl() method:
bool HandleTopLevelDecl(DeclGroupRef DG) override {
for (DeclGroupRef::iterator I = DG.begin(),
E = DG.end();
I != E; ++I) {
const Decl *D = *I;
if (const FunctionDecl *FD =
dyn_cast(D)) {
- After finding a function declaration, you’ll need to retrieve the name of the function. You’ll also need to make sure that the name is not empty:
std::string Name = FD->getNameInfo().getName().getAsString(); assert(Name.length() > 0 && "Unexpected empty identifier");