]> git.lizzy.rs Git - rust.git/blob - src/doc/book/compiler-plugins.md
Changed issue number to 36105
[rust.git] / src / doc / book / compiler-plugins.md
1 % Compiler Plugins
2
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
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
11 `rustc_plugin` documentation for more about the
12 mechanics of defining and loading a plugin.
13
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.
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.
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 at
34 compile time.
35
36 Let's write a plugin
37 [`roman_numerals.rs`](https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs)
38 that implements Roman numeral integer literals.
39
40 ```rust,ignore
41 #![crate_type="dylib"]
42 #![feature(plugin_registrar, rustc_private)]
43
44 extern crate syntax;
45 extern crate rustc;
46 extern crate rustc_plugin;
47
48 use syntax::parse::token;
49 use syntax::ast::TokenTree;
50 use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
51 use syntax::ext::build::AstBuilder;  // trait for expr_usize
52 use syntax_pos::Span;
53 use rustc_plugin::Registry;
54
55 fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
56         -> Box<MacResult + 'static> {
57
58     static NUMERALS: &'static [(&'static str, usize)] = &[
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
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(),
73         _ => {
74             cx.span_err(sp, "argument should be a single identifier");
75             return DummyResult::any(sp);
76         }
77     };
78
79     let mut text = &*text;
80     let mut total = 0;
81     while !text.is_empty() {
82         match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) {
83             Some(&(rn, val)) => {
84                 total += val;
85                 text = &text[rn.len()..];
86             }
87             None => {
88                 cx.span_err(sp, "invalid Roman numeral");
89                 return DummyResult::any(sp);
90             }
91         }
92     }
93
94     MacEager::expr(cx.expr_usize(sp, total))
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 ```rust,ignore
106 #![feature(plugin)]
107 #![plugin(roman_numerals)]
108
109 fn main() {
110     assert_eq!(rn!(MMXV), 2015);
111 }
112 ```
113
114 The advantages over a simple `fn(&str) -> u32` are:
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
122 [`derive`](../reference.html#derive)-like attributes and other kinds of
123 extensions.  See `Registry::register_syntax_extension` and the `SyntaxExtension`
124 enum.  For a more involved macro example, see
125 [`regex_macros`](https://github.com/rust-lang/regex/blob/master/regex_macros/src/lib.rs).
126
127
128 ## Tips and tricks
129
130 Some of the [macro debugging tips](macros.html#debugging-macro-code) are applicable.
131
132 You can use `syntax::parse` to turn token trees into
133 higher-level syntax elements like expressions:
134
135 ```rust,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
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`.
159 As an alternative to the `AstBuilder` trait, `libsyntax` provides a set of
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.
163
164
165 # Lint plugins
166
167 Plugins can extend [Rust's lint
168 infrastructure](../reference.html#lint-check-attributes) with additional checks for
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)
171 that warns about any item named `lintme`.
172
173 ```rust,ignore
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;
182 extern crate rustc_plugin;
183
184 use rustc::lint::{EarlyContext, LintContext, LintPass, EarlyLintPass,
185                   EarlyLintPassObject, LintArray};
186 use rustc_plugin::Registry;
187 use syntax::ast;
188
189 declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
190
191 struct Pass;
192
193 impl LintPass for Pass {
194     fn get_lints(&self) -> LintArray {
195         lint_array!(TEST_LINT)
196     }
197 }
198
199 impl EarlyLintPass for Pass {
200     fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
201         if it.ident.name.as_str() == "lintme" {
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) {
209     reg.register_early_lint_pass(box Pass as EarlyLintPassObject);
210 }
211 ```
212
213 Then code like
214
215 ```rust,ignore
216 #![plugin(lint_plugin_test)]
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
231 * one or more `declare_lint!` invocations, which define static `Lint` structs;
232
233 * a struct holding any state needed by the lint pass (here, none);
234
235 * a `LintPass`
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`.