]> git.lizzy.rs Git - rust.git/blob - src/doc/unstable-book/src/language-features/plugin.md
19ece09509078d450c11f6a1efe0f09d12900139
[rust.git] / src / doc / unstable-book / src / language-features / plugin.md
1 # `plugin`
2
3 The tracking issue for this feature is: [#29597]
4
5 [#29597]: https://github.com/rust-lang/rust/issues/29597
6
7
8 This feature is part of "compiler plugins." It will often be used with the
9 [`plugin_registrar`] and `rustc_private` features.
10
11 [`plugin_registrar`]: language-features/plugin-registrar.html
12
13 ------------------------
14
15 `rustc` can load compiler plugins, which are user-provided libraries that
16 extend the compiler's behavior with new syntax extensions, lint checks, etc.
17
18 A plugin is a dynamic library crate with a designated *registrar* function that
19 registers extensions with `rustc`. Other crates can load these extensions using
20 the crate attribute `#![plugin(...)]`.  See the
21 `rustc_plugin` documentation for more about the
22 mechanics of defining and loading a plugin.
23
24 If present, arguments passed as `#![plugin(foo(... args ...))]` are not
25 interpreted by rustc itself.  They are provided to the plugin through the
26 `Registry`'s `args` method.
27
28 In the vast majority of cases, a plugin should *only* be used through
29 `#![plugin]` and not through an `extern crate` item.  Linking a plugin would
30 pull in all of libsyntax and librustc as dependencies of your crate.  This is
31 generally unwanted unless you are building another plugin.  The
32 `plugin_as_library` lint checks these guidelines.
33
34 The usual practice is to put compiler plugins in their own crate, separate from
35 any `macro_rules!` macros or ordinary Rust code meant to be used by consumers
36 of a library.
37
38 # Syntax extensions
39
40 Plugins can extend Rust's syntax in various ways. One kind of syntax extension
41 is the procedural macro. These are invoked the same way as [ordinary
42 macros](../book/macros.html), but the expansion is performed by arbitrary Rust
43 code that manipulates syntax trees at
44 compile time.
45
46 Let's write a plugin
47 [`roman_numerals.rs`](https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs)
48 that implements Roman numeral integer literals.
49
50 ```rust,ignore
51 #![crate_type="dylib"]
52 #![feature(plugin_registrar, rustc_private)]
53
54 extern crate syntax;
55 extern crate rustc;
56 extern crate rustc_plugin;
57
58 use syntax::parse::token;
59 use syntax::tokenstream::TokenTree;
60 use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
61 use syntax::ext::build::AstBuilder;  // A trait for expr_usize.
62 use syntax::ext::quote::rt::Span;
63 use rustc_plugin::Registry;
64
65 fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
66         -> Box<MacResult + 'static> {
67
68     static NUMERALS: &'static [(&'static str, usize)] = &[
69         ("M", 1000), ("CM", 900), ("D", 500), ("CD", 400),
70         ("C",  100), ("XC",  90), ("L",  50), ("XL",  40),
71         ("X",   10), ("IX",   9), ("V",   5), ("IV",   4),
72         ("I",    1)];
73
74     if args.len() != 1 {
75         cx.span_err(
76             sp,
77             &format!("argument should be a single identifier, but got {} arguments", args.len()));
78         return DummyResult::any(sp);
79     }
80
81     let text = match args[0] {
82         TokenTree::Token(_, token::Ident(s)) => s.to_string(),
83         _ => {
84             cx.span_err(sp, "argument should be a single identifier");
85             return DummyResult::any(sp);
86         }
87     };
88
89     let mut text = &*text;
90     let mut total = 0;
91     while !text.is_empty() {
92         match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) {
93             Some(&(rn, val)) => {
94                 total += val;
95                 text = &text[rn.len()..];
96             }
97             None => {
98                 cx.span_err(sp, "invalid Roman numeral");
99                 return DummyResult::any(sp);
100             }
101         }
102     }
103
104     MacEager::expr(cx.expr_usize(sp, total))
105 }
106
107 #[plugin_registrar]
108 pub fn plugin_registrar(reg: &mut Registry) {
109     reg.register_macro("rn", expand_rn);
110 }
111 ```
112
113 Then we can use `rn!()` like any other macro:
114
115 ```rust,ignore
116 #![feature(plugin)]
117 #![plugin(roman_numerals)]
118
119 fn main() {
120     assert_eq!(rn!(MMXV), 2015);
121 }
122 ```
123
124 The advantages over a simple `fn(&str) -> u32` are:
125
126 * The (arbitrarily complex) conversion is done at compile time.
127 * Input validation is also performed at compile time.
128 * It can be extended to allow use in patterns, which effectively gives
129   a way to define new literal syntax for any data type.
130
131 In addition to procedural macros, you can define new
132 [`derive`](../reference/attributes.html#derive)-like attributes and other kinds
133 of extensions.  See `Registry::register_syntax_extension` and the
134 `SyntaxExtension` enum.  For a more involved macro example, see
135 [`regex_macros`](https://github.com/rust-lang/regex/blob/master/regex_macros/src/lib.rs).
136
137
138 ## Tips and tricks
139
140 Some of the [macro debugging tips](../book/first-edition/macros.html#debugging-macro-code) are applicable.
141
142 You can use `syntax::parse` to turn token trees into
143 higher-level syntax elements like expressions:
144
145 ```rust,ignore
146 fn expand_foo(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
147         -> Box<MacResult+'static> {
148
149     let mut parser = cx.new_parser_from_tts(args);
150
151     let expr: P<Expr> = parser.parse_expr();
152 ```
153
154 Looking through [`libsyntax` parser
155 code](https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/parser.rs)
156 will give you a feel for how the parsing infrastructure works.
157
158 Keep the `Span`s of everything you parse, for better error reporting. You can
159 wrap `Spanned` around your custom data structures.
160
161 Calling `ExtCtxt::span_fatal` will immediately abort compilation. It's better to
162 instead call `ExtCtxt::span_err` and return `DummyResult` so that the compiler
163 can continue and find further errors.
164
165 To print syntax fragments for debugging, you can use `span_note` together with
166 `syntax::print::pprust::*_to_string`.
167
168 The example above produced an integer literal using `AstBuilder::expr_usize`.
169 As an alternative to the `AstBuilder` trait, `libsyntax` provides a set of
170 quasiquote macros. They are undocumented and very rough around the edges.
171 However, the implementation may be a good starting point for an improved
172 quasiquote as an ordinary plugin library.
173
174
175 # Lint plugins
176
177 Plugins can extend [Rust's lint
178 infrastructure](../reference/attributes.html#lint-check-attributes) with
179 additional checks for code style, safety, etc. Now let's write a plugin
180 [`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint_plugin_test.rs)
181 that warns about any item named `lintme`.
182
183 ```rust,ignore
184 #![feature(plugin_registrar)]
185 #![feature(box_syntax, rustc_private)]
186 #![feature(macro_vis_matcher)]
187 #![feature(macro_at_most_once_rep)]
188
189 extern crate syntax;
190
191 // Load rustc as a plugin to get macros
192 #[macro_use]
193 extern crate rustc;
194 extern crate rustc_plugin;
195
196 use rustc::lint::{EarlyContext, LintContext, LintPass, EarlyLintPass,
197                   EarlyLintPassObject, LintArray};
198 use rustc_plugin::Registry;
199 use syntax::ast;
200
201 declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
202
203 struct Pass;
204
205 impl LintPass for Pass {
206     fn get_lints(&self) -> LintArray {
207         lint_array!(TEST_LINT)
208     }
209 }
210
211 impl EarlyLintPass for Pass {
212     fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
213         if it.ident.as_str() == "lintme" {
214             cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'");
215         }
216     }
217 }
218
219 #[plugin_registrar]
220 pub fn plugin_registrar(reg: &mut Registry) {
221     reg.register_early_lint_pass(box Pass as EarlyLintPassObject);
222 }
223 ```
224
225 Then code like
226
227 ```rust,ignore
228 #![plugin(lint_plugin_test)]
229
230 fn lintme() { }
231 ```
232
233 will produce a compiler warning:
234
235 ```txt
236 foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default
237 foo.rs:4 fn lintme() { }
238          ^~~~~~~~~~~~~~~
239 ```
240
241 The components of a lint plugin are:
242
243 * one or more `declare_lint!` invocations, which define static `Lint` structs;
244
245 * a struct holding any state needed by the lint pass (here, none);
246
247 * a `LintPass`
248   implementation defining how to check each syntax element. A single
249   `LintPass` may call `span_lint` for several different `Lint`s, but should
250   register them all through the `get_lints` method.
251
252 Lint passes are syntax traversals, but they run at a late stage of compilation
253 where type information is available. `rustc`'s [built-in
254 lints](https://github.com/rust-lang/rust/blob/master/src/librustc/lint/builtin.rs)
255 mostly use the same infrastructure as lint plugins, and provide examples of how
256 to access type information.
257
258 Lints defined by plugins are controlled by the usual [attributes and compiler
259 flags](../reference/attributes.html#lint-check-attributes), e.g.
260 `#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the
261 first argument to `declare_lint!`, with appropriate case and punctuation
262 conversion.
263
264 You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,
265 including those provided by plugins loaded by `foo.rs`.