]> git.proxmox.com Git - rustc.git/blame - src/doc/book/compiler-plugins.md
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / doc / book / compiler-plugins.md
CommitLineData
85aaf69f 1% Compiler Plugins
1a4d82fc 2
1a4d82fc
JJ
3# Introduction
4
5`rustc` can load compiler plugins, which are user-provided libraries that
6extend the compiler's behavior with new syntax extensions, lint checks, etc.
7
85aaf69f
SL
8A plugin is a dynamic library crate with a designated *registrar* function that
9registers extensions with `rustc`. Other crates can load these extensions using
10the crate attribute `#![plugin(...)]`. See the
54a0048b 11`rustc_plugin` documentation for more about the
1a4d82fc
JJ
12mechanics of defining and loading a plugin.
13
85aaf69f
SL
14If present, arguments passed as `#![plugin(foo(... args ...))]` are not
15interpreted by rustc itself. They are provided to the plugin through the
54a0048b 16`Registry`'s `args` method.
85aaf69f
SL
17
18In 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
20pull in all of libsyntax and librustc as dependencies of your crate. This is
21generally unwanted unless you are building another plugin. The
22`plugin_as_library` lint checks these guidelines.
23
24The usual practice is to put compiler plugins in their own crate, separate from
25any `macro_rules!` macros or ordinary Rust code meant to be used by consumers
26of a library.
1a4d82fc
JJ
27
28# Syntax extensions
29
30Plugins can extend Rust's syntax in various ways. One kind of syntax extension
31is the procedural macro. These are invoked the same way as [ordinary
32macros](macros.html), but the expansion is performed by arbitrary Rust
54a0048b 33code that manipulates syntax trees at
1a4d82fc
JJ
34compile time.
35
36Let's write a plugin
37[`roman_numerals.rs`](https://github.com/rust-lang/rust/tree/master/src/test/auxiliary/roman_numerals.rs)
38that implements Roman numeral integer literals.
39
40```ignore
41#![crate_type="dylib"]
c34b1796 42#![feature(plugin_registrar, rustc_private)]
1a4d82fc
JJ
43
44extern crate syntax;
45extern crate rustc;
92a42be0 46extern crate rustc_plugin;
1a4d82fc
JJ
47
48use syntax::codemap::Span;
49use syntax::parse::token;
92a42be0 50use syntax::ast::TokenTree;
c34b1796
AL
51use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
52use syntax::ext::build::AstBuilder; // trait for expr_usize
92a42be0 53use rustc_plugin::Registry;
1a4d82fc
JJ
54
55fn 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]
98pub fn plugin_registrar(reg: &mut Registry) {
99 reg.register_macro("rn", expand_rn);
100}
101```
102
103Then we can use `rn!()` like any other macro:
104
105```ignore
106#![feature(plugin)]
85aaf69f 107#![plugin(roman_numerals)]
1a4d82fc
JJ
108
109fn main() {
110 assert_eq!(rn!(MMXV), 2015);
111}
112```
113
85aaf69f 114The 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
121In addition to procedural macros, you can define new
85aaf69f 122[`derive`](../reference.html#derive)-like attributes and other kinds of
54a0048b
SL
123extensions. See `Registry::register_syntax_extension` and the `SyntaxExtension`
124enum. 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 130Some of the [macro debugging tips](macros.html#debugging-macro-code) are applicable.
1a4d82fc 131
54a0048b 132You can use `syntax::parse` to turn token trees into
1a4d82fc
JJ
133higher-level syntax elements like expressions:
134
135```ignore
136fn 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
144Looking through [`libsyntax` parser
145code](https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/parser.rs)
146will give you a feel for how the parsing infrastructure works.
147
54a0048b
SL
148Keep the `Span`s of everything you parse, for better error reporting. You can
149wrap `Spanned` around your custom data structures.
150
151Calling `ExtCtxt::span_fatal` will immediately abort compilation. It's better to
152instead call `ExtCtxt::span_err` and return `DummyResult` so that the compiler
153can continue and find further errors.
154
155To print syntax fragments for debugging, you can use `span_note` together with
156`syntax::print::pprust::*_to_string`.
157
158The example above produced an integer literal using `AstBuilder::expr_usize`.
1a4d82fc 159As an alternative to the `AstBuilder` trait, `libsyntax` provides a set of
54a0048b
SL
160quasiquote macros. They are undocumented and very rough around the edges.
161However, the implementation may be a good starting point for an improved
162quasiquote as an ordinary plugin library.
1a4d82fc
JJ
163
164
165# Lint plugins
166
167Plugins can extend [Rust's lint
168infrastructure](../reference.html#lint-check-attributes) with additional checks for
b039eaaf
SL
169code 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)
170that warns about any item named `lintme`.
1a4d82fc
JJ
171
172```ignore
b039eaaf
SL
173#![feature(plugin_registrar)]
174#![feature(box_syntax, rustc_private)]
175
176extern crate syntax;
177
178// Load rustc as a plugin to get macros
179#[macro_use]
180extern crate rustc;
92a42be0 181extern crate rustc_plugin;
b039eaaf
SL
182
183use rustc::lint::{EarlyContext, LintContext, LintPass, EarlyLintPass,
184 EarlyLintPassObject, LintArray};
92a42be0 185use rustc_plugin::Registry;
b039eaaf
SL
186use syntax::ast;
187
188declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
1a4d82fc
JJ
189
190struct Pass;
191
192impl LintPass for Pass {
193 fn get_lints(&self) -> LintArray {
194 lint_array!(TEST_LINT)
195 }
b039eaaf 196}
1a4d82fc 197
b039eaaf
SL
198impl 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]
207pub fn plugin_registrar(reg: &mut Registry) {
b039eaaf 208 reg.register_early_lint_pass(box Pass as EarlyLintPassObject);
1a4d82fc
JJ
209}
210```
211
212Then code like
213
214```ignore
85aaf69f 215#![plugin(lint_plugin_test)]
1a4d82fc
JJ
216
217fn lintme() { }
218```
219
220will produce a compiler warning:
221
222```txt
223foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default
224foo.rs:4 fn lintme() { }
225 ^~~~~~~~~~~~~~~
226```
227
228The 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
239Lint passes are syntax traversals, but they run at a late stage of compilation
240where type information is available. `rustc`'s [built-in
241lints](https://github.com/rust-lang/rust/blob/master/src/librustc/lint/builtin.rs)
242mostly use the same infrastructure as lint plugins, and provide examples of how
243to access type information.
244
245Lints defined by plugins are controlled by the usual [attributes and compiler
246flags](../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
250You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,
251including those provided by plugins loaded by `foo.rs`.