diff --git a/configure b/configure
index 1a2a79cb36b..deb55bfb0dd 100755
--- a/configure
+++ b/configure
@@ -926,6 +926,7 @@ do
make_dir $h/test/doc-guide-pointers
make_dir $h/test/doc-guide-container
make_dir $h/test/doc-guide-tasks
+ make_dir $h/test/doc-guide-plugin
make_dir $h/test/doc-rust
done
diff --git a/mk/docs.mk b/mk/docs.mk
index b7a614bfa02..26439948aa4 100644
--- a/mk/docs.mk
+++ b/mk/docs.mk
@@ -27,7 +27,7 @@
######################################################################
DOCS := index intro tutorial guide guide-ffi guide-macros guide-lifetimes \
guide-tasks guide-container guide-pointers guide-testing \
- guide-runtime complement-bugreport \
+ guide-runtime guide-plugin complement-bugreport \
complement-lang-faq complement-design-faq complement-project-faq rust \
rustdoc guide-unsafe guide-strings reference
diff --git a/src/doc/guide-plugin.md b/src/doc/guide-plugin.md
new file mode 100644
index 00000000000..3830a2126e1
--- /dev/null
+++ b/src/doc/guide-plugin.md
@@ -0,0 +1,259 @@
+% The Rust Compiler Plugins Guide
+
+
+
+
+Warning: Plugins are an advanced, unstable feature! For many details,
+the only available documentation is the libsyntax
and librustc
API docs, or even the source
+code itself. These internal compiler APIs are also subject to change at any
+time.
+
+
+
+For defining new syntax it is often much easier to use Rust's built-in macro system.
+
+
+
+The code in this document uses language features not covered in the Rust
+Guide. See the Reference Manual for more
+information.
+
+
+
+
+# Introduction
+
+`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`. Other crates can use these extensions by
+loading the plugin crate with `#[phase(plugin)] extern crate`. See the
+[`rustc::plugin`](rustc/plugin/index.html) documentation for more about the
+mechanics of defining and loading a plugin.
+
+# Syntax extensions
+
+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](guide-macros.html), but the expansion is performed by arbitrary Rust
+code that manipulates [syntax trees](syntax/ast/index.html) at
+compile time.
+
+Let's write a plugin
+[`roman_numerals.rs`](https://github.com/rust-lang/rust/tree/master/src/test/auxiliary/roman_numerals.rs)
+that implements Roman numeral integer literals.
+
+```ignore
+#![crate_type="dylib"]
+#![feature(plugin_registrar)]
+
+extern crate syntax;
+extern crate rustc;
+
+use syntax::codemap::Span;
+use syntax::parse::token::{IDENT, get_ident};
+use syntax::ast::{TokenTree, TTTok};
+use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacExpr};
+use syntax::ext::build::AstBuilder; // trait for expr_uint
+use rustc::plugin::Registry;
+
+fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
+ -> Box {
+
+ static NUMERALS: &'static [(&'static str, uint)] = &[
+ ("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 {
+ [TTTok(_, IDENT(s, _))] => get_ident(s).to_string(),
+ _ => {
+ cx.span_err(sp, "argument should be a single identifier");
+ return DummyResult::any(sp);
+ }
+ };
+
+ let mut text = text.as_slice();
+ let mut total = 0u;
+ while !text.is_empty() {
+ match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) {
+ Some(&(rn, val)) => {
+ total += val;
+ text = text.slice_from(rn.len());
+ }
+ None => {
+ cx.span_err(sp, "invalid Roman numeral");
+ return DummyResult::any(sp);
+ }
+ }
+ }
+
+ MacExpr::new(cx.expr_uint(sp, total))
+}
+
+#[plugin_registrar]
+pub fn plugin_registrar(reg: &mut Registry) {
+ reg.register_macro("rn", expand_rn);
+}
+```
+
+Then we can use `rn!()` like any other macro:
+
+```ignore
+#![feature(phase)]
+
+#[phase(plugin)]
+extern crate roman_numerals;
+
+fn main() {
+ assert_eq!(rn!(MMXV), 2015);
+}
+```
+
+The advantages over a simple `fn(&str) -> uint` are:
+
+* The (arbitrarily complex) conversion is done at compile time.
+* Input validation is also performed at compile time.
+* It can be extended to allow use in patterns, which effectively gives
+ a way to define new literal syntax for any data type.
+
+In addition to procedural macros, you can define new
+[`deriving`](reference.html#deriving)-like attributes and other kinds of
+extensions. See
+[`Registry::register_syntax_extension`](rustc/plugin/registry/struct.Registry.html#method.register_syntax_extension)
+and the [`SyntaxExtension`
+enum](http://doc.rust-lang.org/syntax/ext/base/enum.SyntaxExtension.html). For
+a more involved macro example, see
+[`src/libregex_macros/lib.rs`](https://github.com/rust-lang/rust/blob/master/src/libregex_macros/lib.rs)
+in the Rust distribution.
+
+
+## Tips and tricks
+
+To see the results of expanding syntax extensions, run
+`rustc --pretty expanded`. The output represents a whole crate, so you
+can also feed it back in to `rustc`, which will sometimes produce better
+error messages than the original compilation. Note that the
+`--pretty expanded` output may have a different meaning if multiple
+variables of the same name (but different syntax contexts) are in play
+in the same scope. In this case `--pretty expanded,hygiene` will tell
+you about the syntax contexts.
+
+You can use [`syntax::parse`](syntax/parse/index.html) to turn token trees into
+higher-level syntax elements like expressions:
+
+```ignore
+fn expand_foo(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
+ -> Box {
+
+ let mut parser =
+ parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), args.to_slice())
+
+ let expr: P = parser.parse_expr();
+```
+
+Looking through [`libsyntax` parser
+code](https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/parser.rs)
+will give you a feel for how the parsing infrastructure works.
+
+Keep the [`Span`s](syntax/codemap/struct.Span.html) of
+everything you parse, for better error reporting. You can wrap
+[`Spanned`](syntax/codemap/struct.Spanned.html) around
+your custom data structures.
+
+Calling
+[`ExtCtxt::span_fatal`](syntax/ext/base/struct.ExtCtxt.html#method.span_fatal)
+will immediately abort compilation. It's better to instead call
+[`ExtCtxt::span_err`](syntax/ext/base/struct.ExtCtxt.html#method.span_err)
+and return
+[`DummyResult`](syntax/ext/base/struct.DummyResult.html),
+so that the compiler can continue and find further errors.
+
+The example above produced an integer literal using
+[`AstBuilder::expr_uint`](syntax/ext/build/trait.AstBuilder.html#tymethod.expr_uint).
+As an alternative to the `AstBuilder` trait, `libsyntax` provides a set of
+[quasiquote macros](syntax/ext/quote/index.html). 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.
+
+
+# Lint plugins
+
+Plugins can extend [Rust's lint
+infrastructure](reference.html#lint-check-attributes) with additional checks for
+code style, safety, etc. You can see
+[`src/test/auxiliary/lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/auxiliary/lint_plugin_test.rs)
+for a full example, the core of which is reproduced here:
+
+```ignore
+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
+
+```ignore
+#[phase(plugin)]
+extern crate lint_plugin_test;
+
+fn lintme() { }
+```
+
+will produce a compiler warning:
+
+```txt
+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!` invocations, which define static
+ [`Lint`](rustc/lint/struct.Lint.html) structs;
+
+* a struct holding any state needed by the lint pass (here, none);
+
+* a [`LintPass`](rustc/lint/trait.LintPass.html)
+ implementation defining how to check each syntax element. A single
+ `LintPass` may call `span_lint` for several different `Lint`s, but should
+ register them all through the `get_lints` method.
+
+Lint passes are syntax traversals, but they run at a late stage of compilation
+where type information is available. `rustc`'s [built-in
+lints](https://github.com/rust-lang/rust/blob/master/src/librustc/lint/builtin.rs)
+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](reference.html#lint-check-attributes), e.g. `#[allow(test_lint)]` or
+`-A test-lint`. These identifiers are derived from the first argument to
+`declare_lint!`, with appropriate case and punctuation conversion.
+
+You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,
+including those provided by plugins loaded by `foo.rs`.
diff --git a/src/doc/index.md b/src/doc/index.md
index a956dda63f2..f38c5883b53 100644
--- a/src/doc/index.md
+++ b/src/doc/index.md
@@ -63,6 +63,7 @@ a guide that can help you out:
* [Macros](guide-macros.html)
* [Testing](guide-testing.html)
* [Rust's Runtime](guide-runtime.html)
+* [Compiler Plugins](guide-plugin.html)
# Tools
diff --git a/src/doc/po4a.conf b/src/doc/po4a.conf
index d5e386325cb..54da9bfa716 100644
--- a/src/doc/po4a.conf
+++ b/src/doc/po4a.conf
@@ -13,6 +13,7 @@
[type: text] src/doc/guide-ffi.md $lang:doc/l10n/$lang/guide-ffi.md
[type: text] src/doc/guide-lifetimes.md $lang:doc/l10n/$lang/guide-lifetimes.md
[type: text] src/doc/guide-macros.md $lang:doc/l10n/$lang/guide-macros.md
+[type: text] src/doc/guide-plugin.md $lang:doc/l10n/$lang/guide-plugin.md
[type: text] src/doc/guide-pointers.md $lang:doc/l10n/$lang/guide-pointers.md
[type: text] src/doc/guide-runtime.md $lang:doc/l10n/$lang/guide-runtime.md
[type: text] src/doc/guide-strings.md $lang:doc/l10n/$lang/guide-strings.md
diff --git a/src/librustc/plugin/mod.rs b/src/librustc/plugin/mod.rs
index 71423ee56bc..a03ee471be6 100644
--- a/src/librustc/plugin/mod.rs
+++ b/src/librustc/plugin/mod.rs
@@ -53,8 +53,8 @@
* If you also need the plugin crate available at runtime, use
* `phase(plugin, link)`.
*
- * See `src/test/auxiliary/macro_crate_test.rs` and `src/libfourcc`
- * for examples of syntax extension plugins.
+ * See [the compiler plugin guide](../../guide-plugin.html)
+ * for more examples.
*/
pub use self::registry::Registry;
diff --git a/src/test/auxiliary/roman_numerals.rs b/src/test/auxiliary/roman_numerals.rs
new file mode 100644
index 00000000000..43842fae70f
--- /dev/null
+++ b/src/test/auxiliary/roman_numerals.rs
@@ -0,0 +1,70 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// force-host
+
+#![crate_type="dylib"]
+#![feature(plugin_registrar)]
+
+extern crate syntax;
+extern crate rustc;
+
+use syntax::codemap::Span;
+use syntax::parse::token::{IDENT, get_ident};
+use syntax::ast::{TokenTree, TTTok};
+use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacExpr};
+use syntax::ext::build::AstBuilder; // trait for expr_uint
+use rustc::plugin::Registry;
+
+// WARNING WARNING WARNING WARNING WARNING
+// =======================================
+//
+// This code also appears in src/doc/guide-plugin.md. Please keep
+// the two copies in sync! FIXME: have rustdoc read this file
+
+fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
+ -> Box {
+
+ static NUMERALS: &'static [(&'static str, uint)] = &[
+ ("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 {
+ [TTTok(_, IDENT(s, _))] => get_ident(s).to_string(),
+ _ => {
+ cx.span_err(sp, "argument should be a single identifier");
+ return DummyResult::any(sp);
+ }
+ };
+
+ let mut text = text.as_slice();
+ let mut total = 0u;
+ while !text.is_empty() {
+ match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) {
+ Some(&(rn, val)) => {
+ total += val;
+ text = text.slice_from(rn.len());
+ }
+ None => {
+ cx.span_err(sp, "invalid Roman numeral");
+ return DummyResult::any(sp);
+ }
+ }
+ }
+
+ MacExpr::new(cx.expr_uint(sp, total))
+}
+
+#[plugin_registrar]
+pub fn plugin_registrar(reg: &mut Registry) {
+ reg.register_macro("rn", expand_rn);
+}
diff --git a/src/test/run-pass-fulldeps/roman-numerals-macro.rs b/src/test/run-pass-fulldeps/roman-numerals-macro.rs
new file mode 100644
index 00000000000..6fd427c9f8c
--- /dev/null
+++ b/src/test/run-pass-fulldeps/roman-numerals-macro.rs
@@ -0,0 +1,26 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:roman_numerals.rs
+// ignore-stage1
+
+#![feature(phase)]
+
+#[phase(plugin, link)]
+extern crate roman_numerals;
+
+pub fn main() {
+ assert_eq!(rn!(MMXV), 2015);
+ assert_eq!(rn!(MCMXCIX), 1999);
+ assert_eq!(rn!(XXV), 25);
+ assert_eq!(rn!(MDCLXVI), 1666);
+ assert_eq!(rn!(MMMDCCCLXXXVIII), 3888);
+ assert_eq!(rn!(MMXIV), 2014);
+}