]>
Commit | Line | Data |
---|---|---|
8bb4bdeb | 1 | # `plugin` |
1a4d82fc | 2 | |
8bb4bdeb XL |
3 | The tracking issue for this feature is: [#29597] |
4 | ||
5 | [#29597]: https://github.com/rust-lang/rust/issues/29597 | |
6 | ||
7 | ||
8 | This feature is part of "compiler plugins." It will often be used with the | |
9 | [`plugin_registrar`] and `rustc_private` features. | |
10 | ||
dc9dc135 | 11 | [`plugin_registrar`]: plugin-registrar.md |
8bb4bdeb XL |
12 | |
13 | ------------------------ | |
1a4d82fc JJ |
14 | |
15 | `rustc` can load compiler plugins, which are user-provided libraries that | |
16 | extend the compiler's behavior with new syntax extensions, lint checks, etc. | |
17 | ||
85aaf69f SL |
18 | A plugin is a dynamic library crate with a designated *registrar* function that |
19 | registers extensions with `rustc`. Other crates can load these extensions using | |
20 | the crate attribute `#![plugin(...)]`. See the | |
54a0048b | 21 | `rustc_plugin` documentation for more about the |
1a4d82fc JJ |
22 | mechanics of defining and loading a plugin. |
23 | ||
85aaf69f SL |
24 | If present, arguments passed as `#![plugin(foo(... args ...))]` are not |
25 | interpreted by rustc itself. They are provided to the plugin through the | |
54a0048b | 26 | `Registry`'s `args` method. |
85aaf69f SL |
27 | |
28 | In the vast majority of cases, a plugin should *only* be used through | |
29 | `#![plugin]` and not through an `extern crate` item. Linking a plugin would | |
30 | pull in all of libsyntax and librustc as dependencies of your crate. This is | |
31 | generally unwanted unless you are building another plugin. The | |
32 | `plugin_as_library` lint checks these guidelines. | |
33 | ||
34 | The usual practice is to put compiler plugins in their own crate, separate from | |
35 | any `macro_rules!` macros or ordinary Rust code meant to be used by consumers | |
36 | of a library. | |
1a4d82fc JJ |
37 | |
38 | # Syntax extensions | |
39 | ||
40 | Plugins can extend Rust's syntax in various ways. One kind of syntax extension | |
41 | is the procedural macro. These are invoked the same way as [ordinary | |
dc9dc135 | 42 | macros](../../book/macros.md), but the expansion is performed by arbitrary Rust |
54a0048b | 43 | code that manipulates syntax trees at |
1a4d82fc JJ |
44 | compile time. |
45 | ||
46 | Let's write a plugin | |
416331ca | 47 | [`roman_numerals.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/roman_numerals.rs) |
1a4d82fc JJ |
48 | that implements Roman numeral integer literals. |
49 | ||
a7813a04 | 50 | ```rust,ignore |
1a4d82fc | 51 | #![crate_type="dylib"] |
c34b1796 | 52 | #![feature(plugin_registrar, rustc_private)] |
1a4d82fc JJ |
53 | |
54 | extern crate syntax; | |
9fa01778 | 55 | extern crate syntax_pos; |
1a4d82fc | 56 | extern crate rustc; |
92a42be0 | 57 | extern crate rustc_plugin; |
1a4d82fc | 58 | |
dc9dc135 | 59 | use syntax::parse::token::{self, Token}; |
9e0c209e | 60 | use syntax::tokenstream::TokenTree; |
c34b1796 | 61 | use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager}; |
9fa01778 | 62 | use syntax_pos::Span; |
92a42be0 | 63 | use rustc_plugin::Registry; |
1a4d82fc JJ |
64 | |
65 | fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) | |
dc9dc135 | 66 | -> Box<dyn MacResult + 'static> { |
1a4d82fc | 67 | |
92a42be0 | 68 | static NUMERALS: &'static [(&'static str, usize)] = &[ |
1a4d82fc JJ |
69 | ("M", 1000), ("CM", 900), ("D", 500), ("CD", 400), |
70 | ("C", 100), ("XC", 90), ("L", 50), ("XL", 40), | |
71 | ("X", 10), ("IX", 9), ("V", 5), ("IV", 4), | |
72 | ("I", 1)]; | |
73 | ||
92a42be0 SL |
74 | if args.len() != 1 { |
75 | cx.span_err( | |
76 | sp, | |
77 | &format!("argument should be a single identifier, but got {} arguments", args.len())); | |
78 | return DummyResult::any(sp); | |
79 | } | |
80 | ||
81 | let text = match args[0] { | |
dc9dc135 | 82 | TokenTree::Token(Token { kind: token::Ident(s, _), .. }) => s.to_string(), |
1a4d82fc JJ |
83 | _ => { |
84 | cx.span_err(sp, "argument should be a single identifier"); | |
85 | return DummyResult::any(sp); | |
86 | } | |
87 | }; | |
88 | ||
c34b1796 | 89 | let mut text = &*text; |
85aaf69f | 90 | let mut total = 0; |
1a4d82fc JJ |
91 | while !text.is_empty() { |
92 | match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) { | |
93 | Some(&(rn, val)) => { | |
94 | total += val; | |
c34b1796 | 95 | text = &text[rn.len()..]; |
1a4d82fc JJ |
96 | } |
97 | None => { | |
98 | cx.span_err(sp, "invalid Roman numeral"); | |
99 | return DummyResult::any(sp); | |
100 | } | |
101 | } | |
102 | } | |
103 | ||
92a42be0 | 104 | MacEager::expr(cx.expr_usize(sp, total)) |
1a4d82fc JJ |
105 | } |
106 | ||
107 | #[plugin_registrar] | |
108 | pub fn plugin_registrar(reg: &mut Registry) { | |
109 | reg.register_macro("rn", expand_rn); | |
110 | } | |
111 | ``` | |
112 | ||
113 | Then we can use `rn!()` like any other macro: | |
114 | ||
a7813a04 | 115 | ```rust,ignore |
1a4d82fc | 116 | #![feature(plugin)] |
85aaf69f | 117 | #![plugin(roman_numerals)] |
1a4d82fc JJ |
118 | |
119 | fn main() { | |
120 | assert_eq!(rn!(MMXV), 2015); | |
121 | } | |
122 | ``` | |
123 | ||
85aaf69f | 124 | The advantages over a simple `fn(&str) -> u32` are: |
1a4d82fc JJ |
125 | |
126 | * The (arbitrarily complex) conversion is done at compile time. | |
127 | * Input validation is also performed at compile time. | |
128 | * It can be extended to allow use in patterns, which effectively gives | |
129 | a way to define new literal syntax for any data type. | |
130 | ||
131 | In addition to procedural macros, you can define new | |
dc9dc135 | 132 | [`derive`](../../reference/attributes/derive.md)-like attributes and other kinds |
8bb4bdeb | 133 | of extensions. See `Registry::register_syntax_extension` and the |
dc9dc135 | 134 | `SyntaxExtension` struct. For a more involved macro example, see |
1a4d82fc JJ |
135 | [`regex_macros`](https://github.com/rust-lang/regex/blob/master/regex_macros/src/lib.rs). |
136 | ||
137 | ||
138 | ## Tips and tricks | |
139 | ||
54a0048b | 140 | You can use `syntax::parse` to turn token trees into |
1a4d82fc JJ |
141 | higher-level syntax elements like expressions: |
142 | ||
a7813a04 | 143 | ```rust,ignore |
1a4d82fc JJ |
144 | fn expand_foo(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) |
145 | -> Box<MacResult+'static> { | |
146 | ||
147 | let mut parser = cx.new_parser_from_tts(args); | |
148 | ||
149 | let expr: P<Expr> = parser.parse_expr(); | |
150 | ``` | |
151 | ||
152 | Looking through [`libsyntax` parser | |
153 | code](https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/parser.rs) | |
154 | will give you a feel for how the parsing infrastructure works. | |
155 | ||
54a0048b SL |
156 | Keep the `Span`s of everything you parse, for better error reporting. You can |
157 | wrap `Spanned` around your custom data structures. | |
158 | ||
159 | Calling `ExtCtxt::span_fatal` will immediately abort compilation. It's better to | |
160 | instead call `ExtCtxt::span_err` and return `DummyResult` so that the compiler | |
161 | can continue and find further errors. | |
162 | ||
163 | To print syntax fragments for debugging, you can use `span_note` together with | |
164 | `syntax::print::pprust::*_to_string`. | |
165 | ||
1a4d82fc JJ |
166 | # Lint plugins |
167 | ||
168 | Plugins can extend [Rust's lint | |
dc9dc135 | 169 | infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with |
8bb4bdeb | 170 | additional checks for code style, safety, etc. Now let's write a plugin |
ff7c6d11 | 171 | [`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint_plugin_test.rs) |
b039eaaf | 172 | that warns about any item named `lintme`. |
1a4d82fc | 173 | |
a7813a04 | 174 | ```rust,ignore |
b039eaaf SL |
175 | #![feature(plugin_registrar)] |
176 | #![feature(box_syntax, rustc_private)] | |
177 | ||
178 | extern crate syntax; | |
179 | ||
180 | // Load rustc as a plugin to get macros | |
181 | #[macro_use] | |
182 | extern crate rustc; | |
92a42be0 | 183 | extern crate rustc_plugin; |
b039eaaf SL |
184 | |
185 | use rustc::lint::{EarlyContext, LintContext, LintPass, EarlyLintPass, | |
186 | EarlyLintPassObject, LintArray}; | |
92a42be0 | 187 | use rustc_plugin::Registry; |
b039eaaf SL |
188 | use syntax::ast; |
189 | ||
190 | declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); | |
1a4d82fc JJ |
191 | |
192 | struct Pass; | |
193 | ||
194 | impl LintPass for Pass { | |
195 | fn get_lints(&self) -> LintArray { | |
196 | lint_array!(TEST_LINT) | |
197 | } | |
b039eaaf | 198 | } |
1a4d82fc | 199 | |
b039eaaf SL |
200 | impl EarlyLintPass for Pass { |
201 | fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { | |
94b46f34 | 202 | if it.ident.as_str() == "lintme" { |
1a4d82fc JJ |
203 | cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"); |
204 | } | |
205 | } | |
206 | } | |
207 | ||
208 | #[plugin_registrar] | |
209 | pub fn plugin_registrar(reg: &mut Registry) { | |
b039eaaf | 210 | reg.register_early_lint_pass(box Pass as EarlyLintPassObject); |
1a4d82fc JJ |
211 | } |
212 | ``` | |
213 | ||
214 | Then code like | |
215 | ||
a7813a04 | 216 | ```rust,ignore |
85aaf69f | 217 | #![plugin(lint_plugin_test)] |
1a4d82fc JJ |
218 | |
219 | fn lintme() { } | |
220 | ``` | |
221 | ||
222 | will produce a compiler warning: | |
223 | ||
224 | ```txt | |
225 | foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default | |
226 | foo.rs:4 fn lintme() { } | |
227 | ^~~~~~~~~~~~~~~ | |
228 | ``` | |
229 | ||
230 | The components of a lint plugin are: | |
231 | ||
54a0048b | 232 | * one or more `declare_lint!` invocations, which define static `Lint` structs; |
1a4d82fc JJ |
233 | |
234 | * a struct holding any state needed by the lint pass (here, none); | |
235 | ||
54a0048b | 236 | * a `LintPass` |
1a4d82fc JJ |
237 | implementation defining how to check each syntax element. A single |
238 | `LintPass` may call `span_lint` for several different `Lint`s, but should | |
239 | register them all through the `get_lints` method. | |
240 | ||
241 | Lint passes are syntax traversals, but they run at a late stage of compilation | |
242 | where type information is available. `rustc`'s [built-in | |
243 | lints](https://github.com/rust-lang/rust/blob/master/src/librustc/lint/builtin.rs) | |
244 | mostly use the same infrastructure as lint plugins, and provide examples of how | |
245 | to access type information. | |
246 | ||
247 | Lints defined by plugins are controlled by the usual [attributes and compiler | |
dc9dc135 | 248 | flags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g. |
8bb4bdeb XL |
249 | `#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the |
250 | first argument to `declare_lint!`, with appropriate case and punctuation | |
251 | conversion. | |
1a4d82fc JJ |
252 | |
253 | You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`, | |
254 | including those provided by plugins loaded by `foo.rs`. |