]> git.proxmox.com Git - rustc.git/blame - src/doc/book/compiler-plugins.md
New upstream version 1.12.1+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
3157f602 37[`roman_numerals.rs`](https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs)
1a4d82fc
JJ
38that 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
44extern crate syntax;
45extern crate rustc;
92a42be0 46extern crate rustc_plugin;
1a4d82fc 47
1a4d82fc 48use syntax::parse::token;
92a42be0 49use syntax::ast::TokenTree;
c34b1796
AL
50use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
51use syntax::ext::build::AstBuilder; // trait for expr_usize
3157f602 52use syntax_pos::Span;
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
a7813a04 105```rust,ignore
1a4d82fc 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
a7813a04 135```rust,ignore
1a4d82fc
JJ
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
3157f602
XL
169code 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 171that 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
177extern crate syntax;
178
179// Load rustc as a plugin to get macros
180#[macro_use]
181extern crate rustc;
92a42be0 182extern crate rustc_plugin;
b039eaaf
SL
183
184use rustc::lint::{EarlyContext, LintContext, LintPass, EarlyLintPass,
185 EarlyLintPassObject, LintArray};
92a42be0 186use rustc_plugin::Registry;
b039eaaf
SL
187use syntax::ast;
188
189declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
1a4d82fc
JJ
190
191struct Pass;
192
193impl LintPass for Pass {
194 fn get_lints(&self) -> LintArray {
195 lint_array!(TEST_LINT)
196 }
b039eaaf 197}
1a4d82fc 198
b039eaaf
SL
199impl 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]
208pub fn plugin_registrar(reg: &mut Registry) {
b039eaaf 209 reg.register_early_lint_pass(box Pass as EarlyLintPassObject);
1a4d82fc
JJ
210}
211```
212
213Then code like
214
a7813a04 215```rust,ignore
85aaf69f 216#![plugin(lint_plugin_test)]
1a4d82fc
JJ
217
218fn lintme() { }
219```
220
221will produce a compiler warning:
222
223```txt
224foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default
225foo.rs:4 fn lintme() { }
226 ^~~~~~~~~~~~~~~
227```
228
229The 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
240Lint passes are syntax traversals, but they run at a late stage of compilation
241where type information is available. `rustc`'s [built-in
242lints](https://github.com/rust-lang/rust/blob/master/src/librustc/lint/builtin.rs)
243mostly use the same infrastructure as lint plugins, and provide examples of how
244to access type information.
245
246Lints defined by plugins are controlled by the usual [attributes and compiler
247flags](../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
251You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,
252including those provided by plugins loaded by `foo.rs`.