about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-01-16 16:36:53 -0800
committerbors <bors@rust-lang.org>2014-01-16 16:36:53 -0800
commit80a3f453db387d02dcb596745731031e7a8c9048 (patch)
tree58e6565e927c239a638907b0b173fc90ee24e097 /src/libsyntax
parent61416626acb53143f1b6856cbc0fcbfd9f9b59a8 (diff)
parent328b47d83720f011cb1c95685c3e7df8195efbd3 (diff)
downloadrust-80a3f453db387d02dcb596745731031e7a8c9048.tar.gz
rust-80a3f453db387d02dcb596745731031e7a8c9048.zip
auto merge of #11151 : sfackler/rust/ext-crate, r=alexcrichton
This is a first pass on support for procedural macros that aren't hardcoded into libsyntax. It is **not yet ready to merge** but I've opened a PR to have a chance to discuss some open questions and implementation issues.

Example
=======
Here's a silly example showing off the basics:

my_synext.rs
```rust
#[feature(managed_boxes, globs, macro_registrar, macro_rules)];

extern mod syntax;

use syntax::ast::{Name, token_tree};
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::parse::token;

#[macro_export]
macro_rules! exported_macro (() => (2))

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern(&"make_a_1"),
        NormalTT(@SyntaxExpanderTT {
            expander: SyntaxExpanderTTExpanderWithoutContext(expand_make_a_1),
            span: None,
        } as @SyntaxExpanderTTTrait,
        None));
}

pub fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[token_tree]) -> MacResult {
    if !tts.is_empty() {
        cx.span_fatal(sp, "make_a_1 takes no arguments");
    }
    MRExpr(quote_expr!(cx, 1i))
}
```

main.rs:
```rust
#[feature(phase)];

#[phase(syntax)]
extern mod my_synext;

fn main() {
    assert_eq!(1, make_a_1!());
    assert_eq!(2, exported_macro!());
}
```

Overview
=======
Crates that contain syntax extensions need to define a function with the following signature and annotation:
```rust
#[macro_registrar]
pub fn registrar(register: |ast::Name, ext::base::SyntaxExtension|) { ... }
```
that should call the `register` closure with each extension it defines. `macro_rules!` style macros can be tagged with `#[macro_export]` to be exported from the crate as well.

Crates that wish to use externally loadable syntax extensions load them by adding the `#[phase(syntax)]` attribute to an `extern mod`. All extensions registered by the specified crate are loaded with the same scoping rules as `macro_rules!` macros. If you want to use a crate both for syntax extensions and normal linkage, you can use `#[phase(syntax, link)]`.

Open questions
===========
* ~~Does the `macro_crate` syntax make sense? It wraps an entire `extern mod` declaration which looks a bit weird but is nice in the sense that the crate lookup logic can be identical between normal external crates and external macro crates. If the `extern mod` syntax, changes, this will get it for free, etc.~~ Changed to a `phase` attribute.
* ~~Is the magic name `macro_crate_registration` the right way to handle extension registration? It could alternatively be handled by a function annotated with `#[macro_registration]` I guess.~~ Switched to an attribute.
* The crate loading logic lives inside of librustc, which means that the syntax extension infrastructure can't directly access it. I've worked around this by passing a `CrateLoader` trait object from the driver to libsyntax that can call back into the crate loading logic. It should be possible to pull things apart enough that this isn't necessary anymore, but it will be an enormous refactoring project. I think we'll need to create a couple of new libraries: libsynext libmetadata/ty and libmiddle.
* Item decorator extensions can be loaded but the `deriving` decorator itself can't be extended so you'd need to do e.g. `#[deriving_MyTrait] #[deriving(Clone)]` instead of `#[deriving(MyTrait, Clone)]`. Is this something worth bothering with for now?

Remaining work
===========
- [x] ~~There is not yet support for rustdoc downloading and compiling referenced macro crates as it does for other referenced crates. This shouldn't be too hard I think.~~
- [x] ~~This is not testable at stage1 and sketchily testable at stages above that. The stage *n* rustc links against the stage *n-1* libsyntax and librustc. Unfortunately, crates in the test/auxiliary directory link against the stage *n* libstd, libextra, libsyntax, etc. This causes macro crates to fail to properly dynamically link into rustc since names end up being mangled slightly differently. In addition, when rustc is actually installed onto a system, there are actually do copies of libsyntax, libstd, etc: the ones that user code links against and a separate set from the previous stage that rustc itself uses. By this point in the bootstrap process, the two library versions *should probably* be binary compatible, but it doesn't seem like a sure thing. Fixing this is apparently hard, but necessary to properly cross compile as well and is being tracked in #11145.~~ The offending tests are ignored during `check-stage1-rpass` and `check-stage1-cfail`. When we get a snapshot that has this commit, I'll look into how feasible it'll be to get them working on stage1.
- [x] ~~`macro_rules!` style macros aren't being exported. Now that the crate loading infrastructure is there, this should just require serializing the AST of the macros into the crate metadata and yanking them out again, but I'm not very familiar with that part of the compiler.~~
- [x] ~~The `macro_crate_registration` function isn't type-checked when it's loaded. I poked around in the `csearch` infrastructure a bit but didn't find any super obvious ways of checking the type of an item with a certain name. Fixing this may also eliminate the need to `#[no_mangle]` the registration function.~~ Now that the registration function is identified by an attribute, typechecking this will be like typechecking other annotated functions.
- [x] ~~The dynamic libraries that are loaded are never unloaded. It shouldn't require too much work to tie the lifetime of the `DynamicLibrary` object to the `MapChain` that its extensions are loaded into.~~
- [x] ~~The compiler segfaults sometimes when loading external crates. The `DynamicLibrary` reference and code objects from that library are both put into the same hash table. When the table drops, due to the random ordering the library sometimes drops before the objects do. Once #11228 lands it'll be easy to fix this.~~
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/base.rs111
-rw-r--r--src/libsyntax/ext/build.rs4
-rw-r--r--src/libsyntax/ext/deriving/generic.rs2
-rw-r--r--src/libsyntax/ext/expand.rs110
-rw-r--r--src/libsyntax/ext/format.rs2
-rw-r--r--src/libsyntax/ext/quote.rs2
-rw-r--r--src/libsyntax/ext/registrar.rs60
-rw-r--r--src/libsyntax/fold.rs37
-rw-r--r--src/libsyntax/lib.rs1
9 files changed, 240 insertions, 89 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 8515c3aba50..ef6d154c651 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -20,6 +20,7 @@ use parse::token::{ident_to_str, intern, str_to_ident};
 use util::small_vector::SmallVector;
 
 use std::hashmap::HashMap;
+use std::unstable::dynamic_lib::DynamicLibrary;
 
 // new-style macro! tt code:
 //
@@ -120,6 +121,9 @@ pub type SyntaxExpanderTTItemFun =
 pub type SyntaxExpanderTTItemFunNoCtxt =
     fn(&mut ExtCtxt, Span, ast::Ident, ~[ast::TokenTree]) -> MacResult;
 
+pub type MacroCrateRegistrationFun =
+    extern "Rust" fn(|ast::Name, SyntaxExtension|);
+
 pub trait AnyMacro {
     fn make_expr(&self) -> @ast::Expr;
     fn make_items(&self) -> SmallVector<@ast::Item>;
@@ -151,24 +155,21 @@ pub enum SyntaxExtension {
     IdentTT(~SyntaxExpanderTTItemTrait:'static, Option<Span>),
 }
 
-
-// The SyntaxEnv is the environment that's threaded through the expansion
-// of macros. It contains bindings for macros, and also a special binding
-// for " block" (not a legal identifier) that maps to a BlockInfo
-pub type SyntaxEnv = MapChain<Name, SyntaxExtension>;
-
 pub struct BlockInfo {
     // should macros escape from this scope?
     macros_escape : bool,
     // what are the pending renames?
-    pending_renames : RenameList
+    pending_renames : RenameList,
+    // references for crates loaded in this scope
+    macro_crates: ~[DynamicLibrary],
 }
 
 impl BlockInfo {
     pub fn new() -> BlockInfo {
         BlockInfo {
             macros_escape: false,
-            pending_renames: ~[]
+            pending_renames: ~[],
+            macro_crates: ~[],
         }
     }
 }
@@ -189,7 +190,7 @@ pub fn syntax_expander_table() -> SyntaxEnv {
         None)
     }
 
-    let mut syntax_expanders = MapChain::new();
+    let mut syntax_expanders = SyntaxEnv::new();
     syntax_expanders.insert(intern(&"macro_rules"),
                             IdentTT(~SyntaxExpanderTTItem {
                                 expander: SyntaxExpanderTTItemExpanderWithContext(
@@ -280,25 +281,38 @@ pub fn syntax_expander_table() -> SyntaxEnv {
     syntax_expanders
 }
 
+pub struct MacroCrate {
+    lib: Option<Path>,
+    cnum: ast::CrateNum,
+}
+
+pub trait CrateLoader {
+    fn load_crate(&mut self, crate: &ast::ViewItem) -> MacroCrate;
+    fn get_exported_macros(&mut self, crate_num: ast::CrateNum) -> ~[@ast::Item];
+    fn get_registrar_symbol(&mut self, crate_num: ast::CrateNum) -> Option<~str>;
+}
+
 // One of these is made during expansion and incrementally updated as we go;
 // when a macro expansion occurs, the resulting nodes have the backtrace()
 // -> expn_info of their expansion context stored into their span.
-pub struct ExtCtxt {
+pub struct ExtCtxt<'a> {
     parse_sess: @parse::ParseSess,
     cfg: ast::CrateConfig,
     backtrace: Option<@ExpnInfo>,
+    loader: &'a mut CrateLoader,
 
     mod_path: ~[ast::Ident],
     trace_mac: bool
 }
 
-impl ExtCtxt {
-    pub fn new(parse_sess: @parse::ParseSess, cfg: ast::CrateConfig)
-               -> ExtCtxt {
+impl<'a> ExtCtxt<'a> {
+    pub fn new<'a>(parse_sess: @parse::ParseSess, cfg: ast::CrateConfig,
+               loader: &'a mut CrateLoader) -> ExtCtxt<'a> {
         ExtCtxt {
             parse_sess: parse_sess,
             cfg: cfg,
             backtrace: None,
+            loader: loader,
             mod_path: ~[],
             trace_mac: false
         }
@@ -456,20 +470,27 @@ pub fn get_exprs_from_tts(cx: &ExtCtxt,
 // able to refer to a macro that was added to an enclosing
 // scope lexically later than the deeper scope.
 
-// Only generic to make it easy to test
-struct MapChainFrame<K, V> {
+struct MapChainFrame {
     info: BlockInfo,
-    map: HashMap<K, V>,
+    map: HashMap<Name, SyntaxExtension>,
+}
+
+#[unsafe_destructor]
+impl Drop for MapChainFrame {
+    fn drop(&mut self) {
+        // make sure that syntax extension dtors run before we drop the libs
+        self.map.clear();
+    }
 }
 
 // Only generic to make it easy to test
-pub struct MapChain<K, V> {
-    priv chain: ~[MapChainFrame<K, V>],
+pub struct SyntaxEnv {
+    priv chain: ~[MapChainFrame],
 }
 
-impl<K: Hash+Eq, V> MapChain<K, V> {
-    pub fn new() -> MapChain<K, V> {
-        let mut map = MapChain { chain: ~[] };
+impl SyntaxEnv {
+    pub fn new() -> SyntaxEnv {
+        let mut map = SyntaxEnv { chain: ~[] };
         map.push_frame();
         map
     }
@@ -486,7 +507,7 @@ impl<K: Hash+Eq, V> MapChain<K, V> {
         self.chain.pop();
     }
 
-    fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame<K, V> {
+    fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame {
         for (i, frame) in self.chain.mut_iter().enumerate().invert() {
             if !frame.info.macros_escape || i == 0 {
                 return frame
@@ -495,7 +516,7 @@ impl<K: Hash+Eq, V> MapChain<K, V> {
         unreachable!()
     }
 
-    pub fn find<'a>(&'a self, k: &K) -> Option<&'a V> {
+    pub fn find<'a>(&'a self, k: &Name) -> Option<&'a SyntaxExtension> {
         for frame in self.chain.iter().invert() {
             match frame.map.find(k) {
                 Some(v) => return Some(v),
@@ -505,49 +526,15 @@ impl<K: Hash+Eq, V> MapChain<K, V> {
         None
     }
 
-    pub fn insert(&mut self, k: K, v: V) {
+    pub fn insert(&mut self, k: Name, v: SyntaxExtension) {
         self.find_escape_frame().map.insert(k, v);
     }
 
-    pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo {
-        &mut self.chain[self.chain.len()-1].info
+    pub fn insert_macro_crate(&mut self, lib: DynamicLibrary) {
+        self.find_escape_frame().info.macro_crates.push(lib);
     }
-}
 
-#[cfg(test)]
-mod test {
-    use super::MapChain;
-
-    #[test]
-    fn testenv() {
-        let mut m = MapChain::new();
-        let (a,b,c,d) = ("a", "b", "c", "d");
-        m.insert(1, a);
-        assert_eq!(Some(&a), m.find(&1));
-
-        m.push_frame();
-        m.info().macros_escape = true;
-        m.insert(2, b);
-        assert_eq!(Some(&a), m.find(&1));
-        assert_eq!(Some(&b), m.find(&2));
-        m.pop_frame();
-
-        assert_eq!(Some(&a), m.find(&1));
-        assert_eq!(Some(&b), m.find(&2));
-
-        m.push_frame();
-        m.push_frame();
-        m.info().macros_escape = true;
-        m.insert(3, c);
-        assert_eq!(Some(&c), m.find(&3));
-        m.pop_frame();
-        assert_eq!(Some(&c), m.find(&3));
-        m.pop_frame();
-        assert_eq!(None, m.find(&3));
-
-        m.push_frame();
-        m.insert(4, d);
-        m.pop_frame();
-        assert_eq!(None, m.find(&4));
+    pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo {
+        &mut self.chain[self.chain.len()-1].info
     }
 }
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 85cda0bd1ae..d702ee3ead4 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -236,7 +236,7 @@ pub trait AstBuilder {
                      vis: ast::Visibility, path: ~[ast::Ident]) -> ast::ViewItem;
 }
 
-impl AstBuilder for ExtCtxt {
+impl<'a> AstBuilder for ExtCtxt<'a> {
     fn path(&self, span: Span, strs: ~[ast::Ident]) -> ast::Path {
         self.path_all(span, false, strs, opt_vec::Empty, ~[])
     }
@@ -896,7 +896,7 @@ impl AstBuilder for ExtCtxt {
 }
 
 struct Duplicator<'a> {
-    cx: &'a ExtCtxt,
+    cx: &'a ExtCtxt<'a>,
 }
 
 impl<'a> Folder for Duplicator<'a> {
diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs
index 826d5381d47..202746acddc 100644
--- a/src/libsyntax/ext/deriving/generic.rs
+++ b/src/libsyntax/ext/deriving/generic.rs
@@ -190,7 +190,7 @@ mod ty;
 
 pub struct TraitDef<'a> {
     /// The extension context
-    cx: &'a ExtCtxt,
+    cx: &'a ExtCtxt<'a>,
     /// The span for the current #[deriving(Foo)] header.
     span: Span,
 
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 303277afbe8..b1b38d6dc90 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -30,6 +30,7 @@ use visit::Visitor;
 use util::small_vector::SmallVector;
 
 use std::vec;
+use std::unstable::dynamic_lib::DynamicLibrary;
 
 pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
     match e.node {
@@ -365,13 +366,79 @@ pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
             // yikes... no idea how to apply the mark to this. I'm afraid
             // we're going to have to wait-and-see on this one.
             fld.extsbox.insert(intern(name), ext);
-            SmallVector::zero()
+            if attr::contains_name(it.attrs, "macro_export") {
+                SmallVector::one(it)
+            } else {
+                SmallVector::zero()
+            }
         }
     };
     fld.cx.bt_pop();
     return items;
 }
 
+// load macros from syntax-phase crates
+pub fn expand_view_item(vi: &ast::ViewItem,
+                        fld: &mut MacroExpander)
+                        -> ast::ViewItem {
+    let should_load = vi.attrs.iter().any(|attr| {
+        "phase" == attr.name() &&
+            attr.meta_item_list().map_or(false, |phases| {
+                attr::contains_name(phases, "syntax")
+            })
+    });
+
+    if should_load {
+        load_extern_macros(vi, fld);
+    }
+
+    noop_fold_view_item(vi, fld)
+}
+
+fn load_extern_macros(crate: &ast::ViewItem, fld: &mut MacroExpander) {
+    let MacroCrate { lib, cnum } = fld.cx.loader.load_crate(crate);
+
+    let exported_macros = fld.cx.loader.get_exported_macros(cnum);
+    for &it in exported_macros.iter() {
+        expand_item_mac(it, fld);
+    }
+
+    let path = match lib {
+        Some(path) => path,
+        None => return
+    };
+    // Make sure the path contains a / or the linker will search for it.
+    // If path is already absolute this is a no-op.
+    let path = Path::new(".").join(path);
+
+    let registrar = match fld.cx.loader.get_registrar_symbol(cnum) {
+        Some(registrar) => registrar,
+        None => return
+    };
+
+    let lib = match DynamicLibrary::open(Some(&path)) {
+        Ok(lib) => lib,
+        Err(err) => fld.cx.span_fatal(crate.span, err)
+    };
+
+    unsafe {
+        let registrar: MacroCrateRegistrationFun = match lib.symbol(registrar) {
+            Ok(registrar) => registrar,
+            Err(err) => fld.cx.span_fatal(crate.span, err)
+        };
+        registrar(|name, extension| {
+            let extension = match extension {
+                NormalTT(ext, _) => NormalTT(ext, Some(crate.span)),
+                IdentTT(ext, _) => IdentTT(ext, Some(crate.span)),
+                ItemDecorator(ext) => ItemDecorator(ext),
+            };
+            fld.extsbox.insert(name, extension);
+        });
+    }
+
+    fld.extsbox.insert_macro_crate(lib);
+}
+
 // expand a stmt
 pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
     // why the copying here and not in expand_expr?
@@ -878,7 +945,7 @@ pub fn inject_std_macros(parse_sess: @parse::ParseSess,
 
 pub struct MacroExpander<'a> {
     extsbox: SyntaxEnv,
-    cx: &'a mut ExtCtxt,
+    cx: &'a mut ExtCtxt<'a>,
 }
 
 impl<'a> Folder for MacroExpander<'a> {
@@ -894,6 +961,10 @@ impl<'a> Folder for MacroExpander<'a> {
         expand_item(item, self)
     }
 
+    fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem {
+        expand_view_item(vi, self)
+    }
+
     fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> {
         expand_stmt(stmt, self)
     }
@@ -908,9 +979,10 @@ impl<'a> Folder for MacroExpander<'a> {
 }
 
 pub fn expand_crate(parse_sess: @parse::ParseSess,
+                    loader: &mut CrateLoader,
                     cfg: ast::CrateConfig,
                     c: Crate) -> Crate {
-    let mut cx = ExtCtxt::new(parse_sess, cfg.clone());
+    let mut cx = ExtCtxt::new(parse_sess, cfg.clone(), loader);
     let mut expander = MacroExpander {
         extsbox: syntax_expander_table(),
         cx: &mut cx,
@@ -1076,6 +1148,7 @@ mod test {
     use codemap::Spanned;
     use fold;
     use fold::*;
+    use ext::base::{CrateLoader, MacroCrate};
     use parse;
     use parse::token::{fresh_mark, gensym, intern, ident_to_str};
     use parse::token;
@@ -1119,6 +1192,22 @@ mod test {
         }
     }
 
+    struct ErrLoader;
+
+    impl CrateLoader for ErrLoader {
+        fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
+            fail!("lolwut")
+        }
+
+        fn get_exported_macros(&mut self, _: ast::CrateNum) -> ~[@ast::Item] {
+            fail!("lolwut")
+        }
+
+        fn get_registrar_symbol(&mut self, _: ast::CrateNum) -> Option<~str> {
+            fail!("lolwut")
+        }
+    }
+
     // make sure that fail! is present
     #[test] fn fail_exists_test () {
         let src = @"fn main() { fail!(\"something appropriately gloomy\");}";
@@ -1129,7 +1218,8 @@ mod test {
             ~[],sess);
         let crate_ast = inject_std_macros(sess, ~[], crate_ast);
         // don't bother with striping, doesn't affect fail!.
-        expand_crate(sess,~[],crate_ast);
+        let mut loader = ErrLoader;
+        expand_crate(sess,&mut loader,~[],crate_ast);
     }
 
     // these following tests are quite fragile, in that they don't test what
@@ -1146,7 +1236,8 @@ mod test {
             src,
             ~[],sess);
         // should fail:
-        expand_crate(sess,~[],crate_ast);
+        let mut loader = ErrLoader;
+        expand_crate(sess,&mut loader,~[],crate_ast);
     }
 
     // make sure that macros can leave scope for modules
@@ -1160,7 +1251,8 @@ mod test {
             src,
             ~[],sess);
         // should fail:
-        expand_crate(sess,~[],crate_ast);
+        let mut loader = ErrLoader;
+        expand_crate(sess,&mut loader,~[],crate_ast);
     }
 
     // macro_escape modules shouldn't cause macros to leave scope
@@ -1173,7 +1265,8 @@ mod test {
             src,
             ~[], sess);
         // should fail:
-        expand_crate(sess,~[],crate_ast);
+        let mut loader = ErrLoader;
+        expand_crate(sess,&mut loader,~[],crate_ast);
     }
 
     #[test] fn std_macros_must_parse () {
@@ -1281,7 +1374,8 @@ mod test {
     fn expand_crate_str(crate_str: @str) -> ast::Crate {
         let (crate_ast,ps) = string_to_crate_and_sess(crate_str);
         // the cfg argument actually does matter, here...
-        expand_crate(ps,~[],crate_ast)
+        let mut loader = ErrLoader;
+        expand_crate(ps,&mut loader,~[],crate_ast)
     }
 
     //fn expand_and_resolve(crate_str: @str) -> ast::crate {
diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs
index a4b8dd78403..9ae13ddeb02 100644
--- a/src/libsyntax/ext/format.rs
+++ b/src/libsyntax/ext/format.rs
@@ -34,7 +34,7 @@ enum Position {
 }
 
 struct Context<'a> {
-    ecx: &'a mut ExtCtxt,
+    ecx: &'a mut ExtCtxt<'a>,
     fmtsp: Span,
 
     // Parsed argument expressions and the types that we've found so far for
diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs
index e66e394d639..27f41356a4b 100644
--- a/src/libsyntax/ext/quote.rs
+++ b/src/libsyntax/ext/quote.rs
@@ -243,7 +243,7 @@ pub mod rt {
         fn parse_tts(&self, s: @str) -> ~[ast::TokenTree];
     }
 
-    impl ExtParseUtils for ExtCtxt {
+    impl<'a> ExtParseUtils for ExtCtxt<'a> {
 
         fn parse_item(&self, s: @str) -> @ast::Item {
             let res = parse::parse_item_from_source_str(
diff --git a/src/libsyntax/ext/registrar.rs b/src/libsyntax/ext/registrar.rs
new file mode 100644
index 00000000000..265dd91d7f4
--- /dev/null
+++ b/src/libsyntax/ext/registrar.rs
@@ -0,0 +1,60 @@
+// Copyright 2012-2013 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use ast;
+use attr;
+use codemap::Span;
+use diagnostic;
+use visit;
+use visit::Visitor;
+
+struct MacroRegistrarContext {
+    registrars: ~[(ast::NodeId, Span)],
+}
+
+impl Visitor<()> for MacroRegistrarContext {
+    fn visit_item(&mut self, item: &ast::Item, _: ()) {
+        match item.node {
+            ast::ItemFn(..) => {
+                if attr::contains_name(item.attrs, "macro_registrar") {
+                    self.registrars.push((item.id, item.span));
+                }
+            }
+            _ => {}
+        }
+
+        visit::walk_item(self, item, ());
+    }
+}
+
+pub fn find_macro_registrar(diagnostic: @diagnostic::SpanHandler,
+                            crate: &ast::Crate) -> Option<ast::DefId> {
+    let mut ctx = MacroRegistrarContext { registrars: ~[] };
+    visit::walk_crate(&mut ctx, crate, ());
+
+    match ctx.registrars.len() {
+        0 => None,
+        1 => {
+            let (node_id, _) = ctx.registrars.pop();
+            Some(ast::DefId {
+                crate: ast::LOCAL_CRATE,
+                node: node_id
+            })
+        },
+        _ => {
+            diagnostic.handler().err("Multiple macro registration functions found");
+            for &(_, span) in ctx.registrars.iter() {
+                diagnostic.span_note(span, "one is here");
+            }
+            diagnostic.handler().abort_if_errors();
+            unreachable!();
+        }
+    }
+}
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 35b29783b67..7a86dd6e4ce 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -63,20 +63,7 @@ pub trait Folder {
     }
 
     fn fold_view_item(&mut self, vi: &ViewItem) -> ViewItem {
-        let inner_view_item = match vi.node {
-            ViewItemExternMod(ref ident, string, node_id) => {
-                ViewItemExternMod(ident.clone(), string, self.new_id(node_id))
-            }
-            ViewItemUse(ref view_paths) => {
-                ViewItemUse(self.fold_view_paths(*view_paths))
-            }
-        };
-        ViewItem {
-            node: inner_view_item,
-            attrs: vi.attrs.map(|a| fold_attribute_(*a, self)),
-            vis: vi.vis,
-            span: self.new_span(vi.span),
-        }
+        noop_fold_view_item(vi, self)
     }
 
     fn fold_foreign_item(&mut self, ni: @ForeignItem) -> @ForeignItem {
@@ -509,6 +496,28 @@ fn fold_variant_arg_<T: Folder>(va: &VariantArg, folder: &mut T) -> VariantArg {
     }
 }
 
+pub fn noop_fold_view_item<T: Folder>(vi: &ViewItem, folder: &mut T)
+                                       -> ViewItem{
+    let inner_view_item = match vi.node {
+        ViewItemExternMod(ref ident,
+                             string,
+                             node_id) => {
+            ViewItemExternMod(ident.clone(),
+                                 string,
+                                 folder.new_id(node_id))
+        }
+        ViewItemUse(ref view_paths) => {
+            ViewItemUse(folder.fold_view_paths(*view_paths))
+        }
+    };
+    ViewItem {
+        node: inner_view_item,
+        attrs: vi.attrs.map(|a| fold_attribute_(*a, folder)),
+        vis: vi.vis,
+        span: folder.new_span(vi.span),
+    }
+}
+
 pub fn noop_fold_block<T: Folder>(b: P<Block>, folder: &mut T) -> P<Block> {
     let view_items = b.view_items.map(|x| folder.fold_view_item(x));
     let stmts = b.stmts.iter().flat_map(|s| folder.fold_stmt(*s).move_iter()).collect();
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 7eebbc75f94..532a2a9a314 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -68,6 +68,7 @@ pub mod ext {
     pub mod asm;
     pub mod base;
     pub mod expand;
+    pub mod registrar;
 
     pub mod quote;