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