
Since almost all Google domains support HTTPS, and it is a good practice to use HTTPS wherever possible, thus in this patch, I changed most HTTP links to https whose domains are known to support HTTPS well. Modifications are generated by running these commands in src/docs directory: sed -i 's/http:\/\/www.chromium.org/https:\/\/www.chromium.org/g' *.md sed -i 's/http:\/\/developer.android.com/https:\/\/developer.android.com/g' *.md sed -i 's/http:\/\/dev.chromium.org/https:\/\/dev.chromium.org/g' *.md sed -i 's/http:\/\/build.chromium.org/https:\/\/build.chromium.org/g' *.md sed -i 's/http:\/\/src.chromium.org/https:\/\/src.chromium.org/g' *.md sed -i 's/http:\/\/crbug.com/https:\/\/crbug.com/g' *.md sed -i 's/http:\/\/groups.google.com/https:\/\/groups.google.com/g' *.md sed -i 's/http:\/\/cs.chromium.org/https:\/\/cs.chromium.org/g' *.md sed -i 's/http:\/\/codereview.chromium.org/https:\/\/codereview.chromium.org/g' *.md BUG= Review-Url: https://codereview.chromium.org/2545363002 Cr-Commit-Position: refs/heads/master@{#436501}
161 lines
6.8 KiB
Markdown
161 lines
6.8 KiB
Markdown
# Don't write a clang plugin
|
||
|
||
[TOC]
|
||
|
||
Make sure you really want to write a clang plugin.
|
||
|
||
* The clang plugin api is not stable. If you write a plugin, _you_ are
|
||
responsible for making sure it's updated when we update clang.
|
||
* If you're adding a generally useful warning, it should be added to upstream
|
||
clang, not to a plugin.
|
||
* You should not use a clang plugin to do things that can be done in a
|
||
PRESUBMIT check (e.g. checking that the headers in a file are sorted).
|
||
|
||
Valid reasons for writing a plugin are for example:
|
||
|
||
* You want to add a chromium-specific error message.
|
||
* You want to write an automatic code rewriter.
|
||
|
||
In both cases, please inform
|
||
[clang@chromium.org](https://groups.google.com/a/chromium.org/group/clang/topics)
|
||
of your plans before you pursue them.
|
||
|
||
# Having said that
|
||
|
||
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.
|
||
|
||
# Building your plugin
|
||
|
||
## Just copy the clang build system
|
||
|
||
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](clang.md) on how to use your plugin while building chromium
|
||
with clang.
|
||
|
||
## Use the interface in tools/clang/plugins/ChromeClassTester.h
|
||
|
||
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.
|
||
|
||
## Or if you're doing something really different, copy PrintFunctionNames.cpp
|
||
|
||
`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:
|
||
|
||
```cpp
|
||
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.
|
||
|
||
## Your ASTConsumer
|
||
|
||
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:
|
||
|
||
```cpp
|
||
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.
|
||
|
||
## Emitting 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:
|
||
|
||
```cpp
|
||
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!)
|
||
|
||
## Downcast early, Downcast often
|
||
|
||
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:
|
||
|
||
```cpp
|
||
virtual void HandleTagDeclDefinition(TagDecl* tag) {
|
||
if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) {
|
||
// Do stuff with |record|.
|
||
}
|
||
}
|
||
```
|
||
|
||
## A (not at all exhaustive) list of things you can do with (CXX)RecordDecl
|
||
|
||
* Iterate across all constructors (`CXXRecordDecl::ctor_begin()`,
|
||
`CXXReocrdDecl::ctor_end()`)
|
||
* `CXXRecordDecl::isPOD()`: is this a Plain Old Datatype (a type that has no
|
||
construction or destruction semantics)?
|
||
* Check if certain properties of the class: `CXXRecordDecl::isAbstract()`,
|
||
`CXXRecordDecl::hasTrivialConstructor()`,
|
||
`CXXRecordDecl::hasTrivialDestructor()`, etc.
|
||
* Iterate across all fields/member variables (`RecordDecl::field_begin()`,
|
||
`RecordDecl::field_end()`)
|
||
* Iterate across all of the base classes of a record type
|
||
(`CXXRecordDecl::bases_begin()`, `CXXRecordDecl::bases_end()`)
|
||
* Get the simple string name `NamedDecl::getNameAsString()`. (This method is
|
||
deprecated, but the replacement assert()s on error conditions). (If you had
|
||
`struct One {}`, this method would return "One".)
|
||
|
||
## Modifying existing plugins
|
||
|
||
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.
|