diff --git a/src/librustc/plugin/load.rs b/src/librustc/plugin/load.rs index 3a9083828fc70..64a5a02b34b47 100644 --- a/src/librustc/plugin/load.rs +++ b/src/librustc/plugin/load.rs @@ -21,6 +21,7 @@ use std::collections::HashSet; use syntax::ast; use syntax::attr; use syntax::parse::token; +use syntax::ptr::P; use syntax::visit; use syntax::visit::Visitor; use syntax::attr::AttrMetaMethods; @@ -29,12 +30,17 @@ use syntax::attr::AttrMetaMethods; pub type PluginRegistrarFun = fn(&mut Registry); +pub struct PluginRegistrar { + pub fun: PluginRegistrarFun, + pub args: P, +} + /// Information about loaded plugins. pub struct Plugins { /// Imported macros. pub macros: Vec, /// Registrars, as function pointers. - pub registrars: Vec, + pub registrars: Vec, } struct PluginLoader<'a> { @@ -87,7 +93,7 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> { } // Parse the attributes relating to macro / plugin loading. - let mut load_registrar = false; + let mut plugin_attr = None; let mut macro_selection = Some(HashSet::new()); // None => load all let mut reexport = HashSet::new(); for attr in vi.attrs.iter() { @@ -97,7 +103,12 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> { self.sess.span_err(attr.span, "#[phase] is deprecated; use \ #[macro_use], #[plugin], and/or #[no_link]"); } - "plugin" => load_registrar = true, + "plugin" => { + if plugin_attr.is_some() { + self.sess.span_err(attr.span, "#[plugin] specified multiple times"); + } + plugin_attr = Some(attr.node.value.clone()); + } "macro_use" => { let names = attr.meta_item_list(); if names.is_none() { @@ -145,6 +156,7 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> { Some(sel) => sel.len() != 0 || reexport.len() != 0, None => true, }; + let load_registrar = plugin_attr.is_some(); if load_macros || load_registrar { let pmd = self.reader.read_plugin_metadata(vi); @@ -167,7 +179,11 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> { } if let Some((lib, symbol)) = registrar { - self.dylink_registrar(vi, lib, symbol); + let fun = self.dylink_registrar(vi, lib, symbol); + self.plugins.registrars.push(PluginRegistrar { + fun: fun, + args: plugin_attr.unwrap(), + }); } } @@ -179,7 +195,10 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> { impl<'a> PluginLoader<'a> { // Dynamically link a registrar function into the compiler process. - fn dylink_registrar(&mut self, vi: &ast::ViewItem, path: Path, symbol: String) { + fn dylink_registrar(&mut self, + vi: &ast::ViewItem, + path: Path, + symbol: String) -> PluginRegistrarFun { // Make sure the path contains a / or the linker will search for it. let path = os::make_absolute(&path).unwrap(); @@ -201,13 +220,12 @@ impl<'a> PluginLoader<'a> { Err(err) => self.sess.span_fatal(vi.span, err[]) }; - self.plugins.registrars.push(registrar); - // Intentionally leak the dynamic library. We can't ever unload it // since the library can make things that will live arbitrarily long // (e.g. an @-box cycle or a task). mem::forget(lib); + registrar } } } diff --git a/src/librustc/plugin/registry.rs b/src/librustc/plugin/registry.rs index c5550902f1989..feec97f02da55 100644 --- a/src/librustc/plugin/registry.rs +++ b/src/librustc/plugin/registry.rs @@ -18,6 +18,7 @@ use syntax::ext::base::{IdentTT, Decorator, Modifier, MacroRulesTT}; use syntax::ext::base::{MacroExpanderFn}; use syntax::codemap::Span; use syntax::parse::token; +use syntax::ptr::P; use syntax::ast; use std::collections::HashMap; @@ -35,6 +36,9 @@ pub struct Registry<'a> { /// from the plugin registrar. pub sess: &'a Session, + #[doc(hidden)] + pub args_hidden: Option>, + #[doc(hidden)] pub krate_span: Span, @@ -53,6 +57,7 @@ impl<'a> Registry<'a> { pub fn new(sess: &'a Session, krate: &ast::Crate) -> Registry<'a> { Registry { sess: sess, + args_hidden: None, krate_span: krate.span, syntax_exts: vec!(), lint_passes: vec!(), @@ -60,6 +65,14 @@ impl<'a> Registry<'a> { } } + /// Get the `#[plugin]` attribute used to load this plugin. + /// + /// This gives access to arguments passed via `#[plugin=...]` or + /// `#[plugin(...)]`. + pub fn args<'b>(&'b self) -> &'b P { + self.args_hidden.as_ref().expect("args not set") + } + /// Register a syntax extension of any kind. /// /// This is the most general hook into `libsyntax`'s expansion behavior. diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 261d73b5bf07a..c88ffffecc32d 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -218,7 +218,7 @@ pub fn phase_2_configure_and_expand(sess: &Session, let mut registry = Registry::new(sess, &krate); - time(time_passes, "plugin registration", (), |_| { + time(time_passes, "plugin registration", registrars, |registrars| { if sess.features.borrow().rustc_diagnostic_macros { registry.register_macro("__diagnostic_used", diagnostics::plugin::expand_diagnostic_used); @@ -228,8 +228,9 @@ pub fn phase_2_configure_and_expand(sess: &Session, diagnostics::plugin::expand_build_diagnostic_array); } - for ®istrar in registrars.iter() { - registrar(&mut registry); + for registrar in registrars.into_iter() { + registry.args_hidden = Some(registrar.args); + (registrar.fun)(&mut registry); } }); diff --git a/src/test/auxiliary/plugin_args.rs b/src/test/auxiliary/plugin_args.rs new file mode 100644 index 0000000000000..b90c3f1d727bf --- /dev/null +++ b/src/test/auxiliary/plugin_args.rs @@ -0,0 +1,50 @@ +// Copyright 2015 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 + +#![feature(plugin_registrar)] + +extern crate syntax; +extern crate rustc; + +use std::borrow::ToOwned; +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::build::AstBuilder; +use syntax::ext::base::{TTMacroExpander, ExtCtxt, MacResult, MacExpr, NormalTT}; +use syntax::parse::token; +use syntax::print::pprust; +use syntax::ptr::P; +use rustc::plugin::Registry; + +struct Expander { + args: P, +} + +impl TTMacroExpander for Expander { + fn expand<'cx>(&self, + ecx: &'cx mut ExtCtxt, + sp: Span, + _: &[ast::TokenTree]) -> Box { + + let attr = ecx.attribute(sp, self.args.clone()); + let src = pprust::attribute_to_string(&attr); + let interned = token::intern_and_get_ident(src.as_slice()); + MacExpr::new(ecx.expr_str(sp, interned)) + } +} + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + let args = reg.args().clone(); + reg.register_syntax_extension(token::intern("plugin_args"), + NormalTT(box Expander { args: args, }, None)); +} diff --git a/src/test/compile-fail/multi-plugin-attr.rs b/src/test/compile-fail/multi-plugin-attr.rs new file mode 100644 index 0000000000000..1d98cd26a38f5 --- /dev/null +++ b/src/test/compile-fail/multi-plugin-attr.rs @@ -0,0 +1,15 @@ +// Copyright 2015 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. + +#[plugin] +#[plugin] //~ ERROR #[plugin] specified multiple times +extern crate std; + +fn main() {} diff --git a/src/test/run-pass/plugin-args-1.rs b/src/test/run-pass/plugin-args-1.rs new file mode 100644 index 0000000000000..5a91f603f9681 --- /dev/null +++ b/src/test/run-pass/plugin-args-1.rs @@ -0,0 +1,22 @@ +// Copyright 2015 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:plugin_args.rs +// ignore-stage1 + +#![feature(plugin)] + +#[no_link] +#[plugin] +extern crate plugin_args; + +fn main() { + assert_eq!(plugin_args!(), "#[plugin]"); +} diff --git a/src/test/run-pass/plugin-args-2.rs b/src/test/run-pass/plugin-args-2.rs new file mode 100644 index 0000000000000..d0ac22a529021 --- /dev/null +++ b/src/test/run-pass/plugin-args-2.rs @@ -0,0 +1,22 @@ +// Copyright 2015 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:plugin_args.rs +// ignore-stage1 + +#![feature(plugin)] + +#[no_link] +#[plugin()] +extern crate plugin_args; + +fn main() { + assert_eq!(plugin_args!(), "#[plugin()]"); +} diff --git a/src/test/run-pass/plugin-args-3.rs b/src/test/run-pass/plugin-args-3.rs new file mode 100644 index 0000000000000..7cac8ac57e55c --- /dev/null +++ b/src/test/run-pass/plugin-args-3.rs @@ -0,0 +1,22 @@ +// Copyright 2015 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:plugin_args.rs +// ignore-stage1 + +#![feature(plugin)] + +#[no_link] +#[plugin(hello(there), how(are="you"))] +extern crate plugin_args; + +fn main() { + assert_eq!(plugin_args!(), "#[plugin(hello(there), how(are = \"you\"))]"); +} diff --git a/src/test/run-pass/plugin-args-4.rs b/src/test/run-pass/plugin-args-4.rs new file mode 100644 index 0000000000000..8563c8c178ff8 --- /dev/null +++ b/src/test/run-pass/plugin-args-4.rs @@ -0,0 +1,22 @@ +// Copyright 2015 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:plugin_args.rs +// ignore-stage1 + +#![feature(plugin)] + +#[no_link] +#[plugin="foobar"] +extern crate plugin_args; + +fn main() { + assert_eq!(plugin_args!(), "#[plugin = \"foobar\"]"); +}