]>
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 | |
37 | [`roman_numerals.rs`](https://github.com/rust-lang/rust/tree/master/src/test/auxiliary/roman_numerals.rs) | |
38 | that implements Roman numeral integer literals. | |
39 | ||
40 | ```ignore | |
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 JJ |
47 | |
48 | use syntax::codemap::Span; | |
49 | use syntax::parse::token; | |
92a42be0 | 50 | use syntax::ast::TokenTree; |
c34b1796 AL |
51 | use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager}; |
52 | use syntax::ext::build::AstBuilder; // trait for expr_usize | |
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 | ||
105 | ```ignore | |
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 | ||
135 | ```ignore | |
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 | |
b039eaaf SL |
169 | code style, safety, etc. Now let's write a plugin [`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/auxiliary/lint_plugin_test.rs) |
170 | that warns about any item named `lintme`. | |
1a4d82fc JJ |
171 | |
172 | ```ignore | |
b039eaaf SL |
173 | #![feature(plugin_registrar)] |
174 | #![feature(box_syntax, rustc_private)] | |
175 | ||
176 | extern crate syntax; | |
177 | ||
178 | // Load rustc as a plugin to get macros | |
179 | #[macro_use] | |
180 | extern crate rustc; | |
92a42be0 | 181 | extern crate rustc_plugin; |
b039eaaf SL |
182 | |
183 | use rustc::lint::{EarlyContext, LintContext, LintPass, EarlyLintPass, | |
184 | EarlyLintPassObject, LintArray}; | |
92a42be0 | 185 | use rustc_plugin::Registry; |
b039eaaf SL |
186 | use syntax::ast; |
187 | ||
188 | declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); | |
1a4d82fc JJ |
189 | |
190 | struct Pass; | |
191 | ||
192 | impl LintPass for Pass { | |
193 | fn get_lints(&self) -> LintArray { | |
194 | lint_array!(TEST_LINT) | |
195 | } | |
b039eaaf | 196 | } |
1a4d82fc | 197 | |
b039eaaf SL |
198 | impl EarlyLintPass for Pass { |
199 | fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { | |
200 | if it.ident.name.as_str() == "lintme" { | |
1a4d82fc JJ |
201 | cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"); |
202 | } | |
203 | } | |
204 | } | |
205 | ||
206 | #[plugin_registrar] | |
207 | pub fn plugin_registrar(reg: &mut Registry) { | |
b039eaaf | 208 | reg.register_early_lint_pass(box Pass as EarlyLintPassObject); |
1a4d82fc JJ |
209 | } |
210 | ``` | |
211 | ||
212 | Then code like | |
213 | ||
214 | ```ignore | |
85aaf69f | 215 | #![plugin(lint_plugin_test)] |
1a4d82fc JJ |
216 | |
217 | fn lintme() { } | |
218 | ``` | |
219 | ||
220 | will produce a compiler warning: | |
221 | ||
222 | ```txt | |
223 | foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default | |
224 | foo.rs:4 fn lintme() { } | |
225 | ^~~~~~~~~~~~~~~ | |
226 | ``` | |
227 | ||
228 | The components of a lint plugin are: | |
229 | ||
54a0048b | 230 | * one or more `declare_lint!` invocations, which define static `Lint` structs; |
1a4d82fc JJ |
231 | |
232 | * a struct holding any state needed by the lint pass (here, none); | |
233 | ||
54a0048b | 234 | * a `LintPass` |
1a4d82fc JJ |
235 | implementation defining how to check each syntax element. A single |
236 | `LintPass` may call `span_lint` for several different `Lint`s, but should | |
237 | register them all through the `get_lints` method. | |
238 | ||
239 | Lint passes are syntax traversals, but they run at a late stage of compilation | |
240 | where type information is available. `rustc`'s [built-in | |
241 | lints](https://github.com/rust-lang/rust/blob/master/src/librustc/lint/builtin.rs) | |
242 | mostly use the same infrastructure as lint plugins, and provide examples of how | |
243 | to access type information. | |
244 | ||
245 | Lints defined by plugins are controlled by the usual [attributes and compiler | |
246 | flags](../reference.html#lint-check-attributes), e.g. `#[allow(test_lint)]` or | |
247 | `-A test-lint`. These identifiers are derived from the first argument to | |
248 | `declare_lint!`, with appropriate case and punctuation conversion. | |
249 | ||
250 | You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`, | |
251 | including those provided by plugins loaded by `foo.rs`. |