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