andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 1 | # Don't write a clang plugin |
| 2 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 3 | [TOC] |
| 4 | |
| 5 | TODO: although cs.chromium.org |
| 6 | [finds](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/llvm/) |
| 7 | `src/third_party/llvm`, it |
| 8 | [does not exist in Gitiles](https://chromium.googlesource.com/src/third_party/llvm/). |
| 9 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 10 | Make sure you really want to write a clang plugin. |
| 11 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 12 | * The clang plugin api is not stable. If you write a plugin, _you_ are |
| 13 | responsible for making sure it's updated when we update clang. |
| 14 | * If you're adding a generally useful warning, it should be added to upstream |
| 15 | clang, not to a plugin. |
| 16 | * You should not use a clang plugin to do things that can be done in a |
| 17 | PRESUBMIT check (e.g. checking that the headers in a file are sorted). |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 18 | |
| 19 | Valid reasons for writing a plugin are for example: |
| 20 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 21 | * You want to add a chromium-specific error message. |
| 22 | * You want to write an automatic code rewriter. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 23 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 24 | In both cases, please inform |
| 25 | [clang@chromium.org](http://groups.google.com/a/chromium.org/group/clang/topics) |
| 26 | of your plans before you pursue them. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 27 | |
| 28 | # Having said that |
| 29 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 30 | clang currently has minimal documentation on its plugin interface; it's mostly |
| 31 | doxygen annotations in the source. This is an attempt to be half map to the |
| 32 | header files/half tutorial. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 33 | |
| 34 | # Building your plugin |
| 35 | |
| 36 | ## Just copy the clang build system |
| 37 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 38 | I suggest you make a new dir in `llvm/tools/clang/examples/` and copy the |
| 39 | Makefile from `PrintFunctionNames` there. This way, you'll just leverage the |
| 40 | existing clang build system. You can then build your plugin with |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 41 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 42 | make -C llvm/tools/clang/examples/myplugin |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 43 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 44 | See [Using plugins](clang.md) on how to use your plugin while building chromium |
| 45 | with clang. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 46 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 47 | ## Use the interface in tools/clang/plugins/ChromeClassTester.h |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 48 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 49 | Here's a canned interface that filters code, only passing class definitions in |
| 50 | non-blacklisted headers. The users of `ChromeClassTester` are good code to study |
| 51 | to see what you can do. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 52 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 53 | ## Or if you're doing something really different, copy PrintFunctionNames.cpp |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 54 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 55 | `PrintFunctionNames.cpp` is a plugin in the clang distribution. It is the Hello |
| 56 | World of plugins. As a most basic skeleton, it's a good starting point. Change |
| 57 | all the identifiers that start with `PrintFunction` to your desired name. Take |
| 58 | note of the final line: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 59 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 60 | ```cpp |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 61 | static FrontendPluginRegistry::Add<PrintFunctionNamesAction> |
| 62 | X("print-fns", "print function names"); |
| 63 | ``` |
| 64 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 65 | This registers your `PluginASTAction` with a string plugin name that can be |
| 66 | invoked on the command line. Note that everything else is in an anonymous |
| 67 | namespace; all other symbols aren't exported. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 68 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 69 | Your `PluginASTAction` subclass exists just to build your `ASTConsumer`, which |
| 70 | receives declarations, sort of like a SAX parser. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 71 | |
| 72 | ## Your ASTConsumer |
| 73 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 74 | There is doxygen documentation on when each `ASTConsumer::Handle` method is |
| 75 | called in `llvm/tools/clang/include/clang/AST/ASTConsumer.h`. For this |
| 76 | tutorial, I'll assume you only want to look at type definitions (struct, class, |
| 77 | enum definitions), so we'll start with: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 78 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 79 | ```cpp |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 80 | class TagConsumer : public ASTConsumer { |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 81 | public: |
| 82 | virtual void HandleTagDeclDefinition(TagDecl *D) { |
| 83 | } |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 84 | }; |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 85 | ``` |
| 86 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 87 | The data type passed in is the `Decl`, which is a giant class hierarchy spanning |
| 88 | the following files: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 89 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 90 | * `llvm/tools/clang/include/clang/AST/DeclBase.h`: declares the `Decl` class, |
| 91 | along with some utility classes you won't use. |
| 92 | * `llvm/tools/clang/include/clang/AST/Decl.h`: declares subclasses of `Decl`, |
| 93 | for example, `FunctionDecl` (a function declaration), `TagDecl` (the base class for struct/class/enum/etc), `TypedefDecl`, etc. |
| 94 | * `llvm/tools/clang/include/clang/AST/DeclCXX.h`: C++ specific types. |
| 95 | You'll find most Decl subclasses dealing with templates here, |
| 96 | along with things like `UsingDirectiveDecl`, `CXXConstructorDecl`, etc. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 97 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 98 | The interface on these classes is massive; We'll only cover some of the basics, |
| 99 | but some basics about source location and errors. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 100 | |
| 101 | ## Emitting Errors |
| 102 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 103 | Lots of location information is stored in the `Decl` tree. Most `Decl` |
| 104 | subclasses have multiple methods that return a `SourceLocation`, but lets use |
| 105 | `TagDecl::getInnerLocStart()` as an example. (`SourceLocation` is defined in |
| 106 | `llvm/tools/clang/include/clang/Basic/SourceLocation.h`, for reference.) |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 107 | |
| 108 | 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: |
| 109 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 110 | ```cpp |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 111 | void emitWarning(CompilerInstance& instance, SourceLocation loc, const char* error) { |
| 112 | FullSourceLoc full(loc, instance.getSourceManager()); |
| 113 | unsigned id = instance.getCustomDiagID(Diagnostic::Warning, error); |
| 114 | DiagnosticBuilder B = instance.getDiagnostics().Report(full, id); |
| 115 | } |
| 116 | ``` |
| 117 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 118 | (The above is the simplest error reporting. See |
| 119 | `llvm/tools/clang/include/clang/Basic/Diagnostic.h` for all the things you can |
| 120 | do, like `FixItHint`s if you want to get fancy!) |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 121 | |
| 122 | ## Downcast early, Downcast often |
| 123 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 124 | The clang library will give you the most general types possible. For example |
| 125 | `TagDecl` has comparably minimal interface. The library is designed so you will |
| 126 | be downcasting all the time, and you won't use the standard `dynamic_cast<>()` |
| 127 | builtin to do it. Instead, you'll use llvm/clang's home built RTTI system: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 128 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 129 | ```cpp |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 130 | virtual void HandleTagDeclDefinition(TagDecl* tag) { |
| 131 | if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) { |
| 132 | // Do stuff with |record|. |
| 133 | } |
| 134 | } |
| 135 | ``` |
| 136 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 137 | ## A (not at all exhaustive) list of things you can do with (CXX)RecordDecl |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 138 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 139 | * Iterate across all constructors (`CXXRecordDecl::ctor_begin()`, |
| 140 | `CXXReocrdDecl::ctor_end()`) |
| 141 | * `CXXRecordDecl::isPOD()`: is this a Plain Old Datatype (a type that has no |
| 142 | construction or destruction semantics)? |
| 143 | * Check if certain properties of the class: `CXXRecordDecl::isAbstract()`, |
| 144 | `CXXRecordDecl::hasTrivialConstructor()`, |
| 145 | `CXXRecordDecl::hasTrivialDestructor()`, etc. |
| 146 | * Iterate across all fields/member variables (`RecordDecl::field_begin()`, |
| 147 | `RecordDecl::field_end()`) |
| 148 | * Iterate across all of the base classes of a record type |
| 149 | (`CXXRecordDecl::bases_begin()`, `CXXRecordDecl::bases_end()`) |
| 150 | * Get the simple string name `NamedDecl::getNameAsString()`. (This method is |
| 151 | deprecated, but the replacement assert()s on error conditions). (If you had |
| 152 | `struct One {}`, this method would return "One".) |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 153 | |
| 154 | ## Modifying existing plugins |
| 155 | |
nodir | e7e901b0 | 2015-08-25 15:30:11 | [diff] [blame^] | 156 | If you want to add additional checks to the existing plugins, be sure to add the |
| 157 | new diagnostic behind a flag (there are several examples of this in the plugins |
| 158 | already). The reason for this is that the plugin is bundled with clang, and the |
| 159 | new check will get deployed with the next clang roll. If your check fires, then |
| 160 | the next clang roll would now be blocked on cleaning up the whole codebase for |
| 161 | your check – and even if the check doesn't fire at the moment, maybe that |
| 162 | regresses until the next clang roll happens. If your new check is behind a flag, |
| 163 | then the clang roll can happen first, and you can add the flag to enable your |
| 164 | check after that, and then turn on the check everywhere once you know that the |
| 165 | codebase is clean. |