rustc
`rustc` can load compiler plugins, which are user-provided libraries that
extend the compiler's behavior with new syntax extensions, lint checks, etc.
A plugin is a dynamic library crate with a designated registrar function that
registers extensions with rustc
`rustc. Other crates can load these extensions using the crate attribute
`. Other crates can load these extensions using
the crate attribute #![plugin(...)]
`#![plugin(...)]. See the [
`. See the
rustc::plugin
`rustc::plugin` documentation for more about the
mechanics of defining and loading a plugin.
If present, arguments passed as #![plugin(foo(... args ...))]
`#![plugin(foo(... args ...))]are not interpreted by rustc itself. They are provided to the plugin through the
` are not
interpreted by rustc itself. They are provided to the plugin through the
Registry
`Registry's [
`'s args
`args` method.
In the vast majority of cases, a plugin should only be used through
#![plugin]
`#![plugin]and not through an
` and not through an extern crate
`extern crateitem. Linking a plugin would pull in all of libsyntax and librustc as dependencies of your crate. This is generally unwanted unless you are building another plugin. The
` item. Linking a plugin would
pull in all of libsyntax and librustc as dependencies of your crate. This is
generally unwanted unless you are building another plugin. The
plugin_as_library
`plugin_as_library` lint checks these guidelines.
The usual practice is to put compiler plugins in their own crate, separate from
any macro_rules!
`macro_rules!` macros or ordinary Rust code meant to be used by consumers
of a library.
Plugins can extend Rust's syntax in various ways. One kind of syntax extension is the procedural macro. These are invoked the same way as ordinary macros, but the expansion is performed by arbitrary Rust code that manipulates syntax trees at compile time.
Let's write a plugin
roman_numerals.rs
`roman_numerals.rs`
that implements Roman numeral integer literals.
#![crate_type="dylib"] #![feature(plugin_registrar, rustc_private)] extern crate syntax; extern crate rustc; use syntax::codemap::Span; use syntax::parse::token; use syntax::ast::{TokenTree, TtToken}; use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager}; use syntax::ext::build::AstBuilder; // trait for expr_usize use rustc::plugin::Registry; fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> Box<MacResult + 'static> { static NUMERALS: &'static [(&'static str, u32)] = &[ ("M", 1000), ("CM", 900), ("D", 500), ("CD", 400), ("C", 100), ("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9), ("V", 5), ("IV", 4), ("I", 1)]; let text = match args { [TtToken(_, token::Ident(s, _))] => token::get_ident(s).to_string(), _ => { cx.span_err(sp, "argument should be a single identifier"); return DummyResult::any(sp); } }; let mut text = &*text; let mut total = 0; while !text.is_empty() { match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) { Some(&(rn, val)) => { total += val; text = &text[rn.len()..]; } None => { cx.span_err(sp, "invalid Roman numeral"); return DummyResult::any(sp); } } } MacEager::expr(cx.expr_u32(sp, total)) } #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { reg.register_macro("rn", expand_rn); }
Then we can use rn!()
`rn!()` like any other macro:
#![feature(plugin)] #![plugin(roman_numerals)] fn main() { assert_eq!(rn!(MMXV), 2015); }
The advantages over a simple fn(&str) -> u32
`fn(&str) -> u32` are:
In addition to procedural macros, you can define new
derive
`derive`-like attributes and other kinds of
extensions. See
Registry::register_syntax_extension
`Registry::register_syntax_extension`
and the SyntaxExtension
`SyntaxExtension`
enum. For
a more involved macro example, see
regex_macros
`regex_macros`.
Some of the macro debugging tips are applicable.
You can use syntax::parse
`syntax::parse` to turn token trees into
higher-level syntax elements like expressions:
fn expand_foo(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> Box<MacResult+'static> { let mut parser = cx.new_parser_from_tts(args); let expr: P<Expr> = parser.parse_expr();
Looking through libsyntax
`libsyntax` parser
code
will give you a feel for how the parsing infrastructure works.
Keep the Span
`Span`s of
everything you parse, for better error reporting. You can wrap
Spanned
`Spanned` around
your custom data structures.
Calling
ExtCtxt::span_fatal
`ExtCtxt::span_fatal`
will immediately abort compilation. It's better to instead call
ExtCtxt::span_err
`ExtCtxt::span_err`
and return
DummyResult
`DummyResult`,
so that the compiler can continue and find further errors.
To print syntax fragments for debugging, you can use
span_note
`span_note` together
with
syntax::print::pprust::*_to_string
`syntax::print::pprust::*_to_string`.
The example above produced an integer literal using
AstBuilder::expr_usize
`AstBuilder::expr_usize`.
As an alternative to the AstBuilder
`AstBuildertrait,
` trait, libsyntax
`libsyntax` provides a set of
quasiquote macros. They are undocumented and
very rough around the edges. However, the implementation may be a good
starting point for an improved quasiquote as an ordinary plugin library.
Plugins can extend Rust's lint
infrastructure with additional checks for
code style, safety, etc. You can see
src/test/auxiliary/lint_plugin_test.rs
`src/test/auxiliary/lint_plugin_test.rs`
for a full example, the core of which is reproduced here:
declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); struct Pass; impl LintPass for Pass { fn get_lints(&self) -> LintArray { lint_array!(TEST_LINT) } fn check_item(&mut self, cx: &Context, it: &ast::Item) { let name = token::get_ident(it.ident); if name.get() == "lintme" { cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"); } } } #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { reg.register_lint_pass(box Pass as LintPassObject); }
Then code like
fn main() { #![plugin(lint_plugin_test)] fn lintme() { } }#![plugin(lint_plugin_test)] fn lintme() { }
will produce a compiler warning:
foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default
foo.rs:4 fn lintme() { }
^~~~~~~~~~~~~~~
The components of a lint plugin are:
one or more declare_lint!
`declare_lint!invocations, which define static [
` invocations, which define static
Lint
`Lint` structs;
a struct holding any state needed by the lint pass (here, none);
a LintPass
`LintPass`
implementation defining how to check each syntax element. A single
LintPass
`LintPassmay call
` may call span_lint
`span_lintfor several different
` for several different Lint
`Lints, but should register them all through the
`s, but should
register them all through the get_lints
`get_lints` method.
Lint passes are syntax traversals, but they run at a late stage of compilation
where type information is available. rustc
`rustc`'s built-in
lints
mostly use the same infrastructure as lint plugins, and provide examples of how
to access type information.
Lints defined by plugins are controlled by the usual attributes and compiler
flags, e.g. #[allow(test_lint)]
`#[allow(test_lint)]or
` or
-A test-lint
`-A test-lint. These identifiers are derived from the first argument to
`. These identifiers are derived from the first argument to
declare_lint!
`declare_lint!`, with appropriate case and punctuation conversion.
You can run rustc -W help foo.rs
`rustc -W help foo.rsto see a list of lints known to
` to see a list of lints known to rustc
`rustc, including those provided by plugins loaded by
`,
including those provided by plugins loaded by foo.rs
`foo.rs`.