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