Make sure you really want to write a clang plugin.
Valid reasons for writing a plugin are for example:
In both cases, please inform clang@chromium.org of your plans before you pursue them.
clang currently has minimal documentation on its plugin interface; it's mostly doxygen annotations in the source. This is an attempt to be half map to the header files/half tutorial.
I suggest you make a new dir in llvm/tools/clang/examples/
and copy the Makefile from PrintFunctionNames
there. This way, you'll just leverage the existing clang build system. You can then build your plugin with
make -C llvm/tools/clang/examples/myplugin
See Using plugins on how to use your plugin while building chromium with clang.
Here's a canned interface that filters code, only passing class definitions in non-blacklisted headers. The users of ChromeClassTester
are good code to study to see what you can do.
PrintFunctionNames.cpp
is a plugin in the clang distribution. It is the Hello World of plugins. As a most basic skeleton, it's a good starting point. Change all the identifiers that start with PrintFunction
to your desired name. Take note of the final line:
static FrontendPluginRegistry::Add<PrintFunctionNamesAction> X("print-fns", "print function names");
This registers your PluginASTAction
with a string plugin name that can be invoked on the command line. Note that everything else is in an anonymous namespace; all other symbols aren't exported.
Your PluginASTAction
subclass exists just to build your ASTConsumer
, which receives declarations, sort of like a SAX parser.
There is doxygen documentation on when each ASTConsumer::Handle
method is called in llvm/tools/clang/include/clang/AST/ASTConsumer.h
. For this tutorial, I‘ll assume you only want to look at type definitions (struct, class, enum definitions), so we’ll start with:
class TagConsumer : public ASTConsumer { public: virtual void HandleTagDeclDefinition(TagDecl *D) { } };
The data type passed in is the Decl
, which is a giant class hierarchy spanning the following files:
llvm/tools/clang/include/clang/AST/DeclBase.h
: declares the Decl
class, along with some utility classes you won't use.llvm/tools/clang/include/clang/AST/Decl.h
: declares subclasses of Decl
, for example, FunctionDecl
(a function declaration), TagDecl
(the base class for struct/class/enum/etc), TypedefDecl
, etc.llvm/tools/clang/include/clang/AST/DeclCXX.h
: C++ specific types. You'll find most Decl subclasses dealing with templates here, along with things like UsingDirectiveDecl
, CXXConstructorDecl
, etc.The interface on these classes is massive; We'll only cover some of the basics, but some basics about source location and errors.
Lots of location information is stored in the Decl
tree. Most Decl
subclasses have multiple methods that return a SourceLocation
, but lets use TagDecl::getInnerLocStart()
as an example. (SourceLocation
is defined in llvm/tools/clang/include/clang/Basic/SourceLocation.h
, for reference.)
Errors are emitted to the user through the CompilerInstance
. You will probably want to pass the CompilerInstance
object passed to ASTAction::CreateASTConsumer
to your ASTConsumer subclass for reporting. You interact with the user through the Diagnostic
object. You could report errors to the user like this:
void emitWarning(CompilerInstance& instance, SourceLocation loc, const char* error) { FullSourceLoc full(loc, instance.getSourceManager()); unsigned id = instance.getCustomDiagID(Diagnostic::Warning, error); DiagnosticBuilder B = instance.getDiagnostics().Report(full, id); }
(The above is the simplest error reporting. See llvm/tools/clang/include/clang/Basic/Diagnostic.h
for all the things you can do, like FixItHint
s if you want to get fancy!)
The clang library will give you the most general types possible. For example TagDecl
has comparably minimal interface. The library is designed so you will be downcasting all the time, and you won‘t use the standard dynamic_cast<>()
builtin to do it. Instead, you’ll use llvm/clang's home built RTTI system:
virtual void HandleTagDeclDefinition(TagDecl* tag) { if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) { // Do stuff with |record|. } }
CXXRecordDecl::ctor_begin()
, CXXReocrdDecl::ctor_end()
)CXXRecordDecl::isPOD()
: is this a Plain Old Datatype (a type that has no construction or destruction semantics)?CXXRecordDecl::isAbstract()
, CXXRecordDecl::hasTrivialConstructor()
, CXXRecordDecl::hasTrivialDestructor()
, etc.RecordDecl::field_begin()
, RecordDecl::field_end()
)CXXRecordDecl::bases_begin()
, CXXRecordDecl::bases_end()
)NamedDecl::getNameAsString()
. (This method is deprecated, but the replacement assert()s on error conditions). (If you had struct One {}
, this method would return “One”.)If you want to add additional checks to the existing plugins, be sure to add the new diagnostic behind a flag (there are several examples of this in the plugins already). The reason for this is that the plugin is bundled with clang, and the new check will get deployed with the next clang roll. If your check fires, then the next clang roll would now be blocked on cleaning up the whole codebase for your check – and even if the check doesn't fire at the moment, maybe that regresses until the next clang roll happens. If your new check is behind a flag, then the clang roll can happen first, and you can add the flag to enable your check after that, and then turn on the check everywhere once you know that the codebase is clean.