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