Skip to content

Use a crate attribute to load plugins #22026

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 10, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/doc/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2014,6 +2014,11 @@ type int8_t = i8;
- `no_start` - disable linking to the `native` crate, which specifies the
"start" language item.
- `no_std` - disable linking to the `std` crate.
- `plugin` — load a list of named crates as compiler plugins, e.g.
`#![plugin(foo, bar)]`. Optional arguments for each plugin,
i.e. `#![plugin(foo(... args ...))]`, are provided to the plugin's
registrar function. The `plugin` feature gate is required to use
this attribute.

### Module-only attributes

Expand Down Expand Up @@ -2082,7 +2087,7 @@ On `struct`s:
remove any padding between fields (note that this is very fragile and may
break platforms which require aligned access).

### Macro- and plugin-related attributes
### Macro-related attributes

- `macro_use` on a `mod` — macros defined in this module will be visible in the
module's parent, after this module has been included.
Expand All @@ -2097,13 +2102,8 @@ On `struct`s:

- `macro_export` - export a macro for cross-crate usage.

- `plugin` on an `extern crate` — load this crate as a [compiler
plugin][plugin]. The `plugin` feature gate is required. Any arguments to
the attribute, e.g. `#[plugin=...]` or `#[plugin(...)]`, are provided to the
plugin.

- `no_link` on an `extern crate` — even if we load this crate for macros or
compiler plugins, don't link it into the output.
- `no_link` on an `extern crate` — even if we load this crate for macros, don't
link it into the output.

See the [macros section of the
book](book/macros.html#scoping-and-macro-import/export) for more information on
Expand Down
15 changes: 7 additions & 8 deletions src/doc/trpl/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ information.
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 `#[plugin] extern crate`. See the
registers extensions with `rustc`. Other crates can load these extensions using
the crate attribute `#![plugin(...)]`. See the
[`rustc::plugin`](../rustc/plugin/index.html) documentation for more about the
mechanics of defining and loading a plugin.

Arguments passed as `#[plugin=...]` or `#[plugin(...)]` are not interpreted by
rustc itself. They are provided to the plugin through the `Registry`'s [`args`
method](../rustc/plugin/registry/struct.Registry.html#method.args).
If present, arguments passed as `#![plugin(foo(... args ...))]` are not
interpreted by rustc itself. They are provided to the plugin through the
`Registry`'s [`args` method](../rustc/plugin/registry/struct.Registry.html#method.args).

# Syntax extensions

Expand Down Expand Up @@ -110,8 +110,7 @@ Then we can use `rn!()` like any other macro:

```ignore
#![feature(plugin)]

#[plugin] extern crate roman_numerals;
#![plugin(roman_numerals)]

fn main() {
assert_eq!(rn!(MMXV), 2015);
Expand Down Expand Up @@ -219,7 +218,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
Then code like

```ignore
#[plugin] extern crate lint_plugin_test;
#![plugin(lint_plugin_test)]

fn lintme() { }
```
Expand Down
8 changes: 4 additions & 4 deletions src/librustc/metadata/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use syntax::ast;
use syntax::abi;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::{COMMAND_LINE_SP, Span, mk_sp};
use syntax::codemap::{Span, mk_sp};
use syntax::parse;
use syntax::parse::token::InternedString;
use syntax::parse::token;
Expand Down Expand Up @@ -457,13 +457,13 @@ impl<'a> CrateReader<'a> {
CrateOrString::Krate(c) => {
(self.extract_crate_info(c).unwrap(), c.span)
}
CrateOrString::Str(s) => {
CrateOrString::Str(sp, s) => {
(CrateInfo {
name: s.to_string(),
ident: s.to_string(),
id: ast::DUMMY_NODE_ID,
should_link: true,
}, COMMAND_LINE_SP)
}, sp)
}
};
let target_triple = &self.sess.opts.target_triple[];
Expand Down Expand Up @@ -531,7 +531,7 @@ impl<'a> CrateReader<'a> {
#[derive(Copy)]
pub enum CrateOrString<'a> {
Krate(&'a ast::Item),
Str(&'a str)
Str(Span, &'a str)
}

impl<'a> PluginMetadata<'a> {
Expand Down
97 changes: 55 additions & 42 deletions src/librustc/plugin/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ use std::mem;
use std::env;
use std::dynamic_lib::DynamicLibrary;
use std::collections::HashSet;
use std::borrow::ToOwned;
use syntax::ast;
use syntax::attr;
use syntax::codemap::Span;
use syntax::codemap::{Span, COMMAND_LINE_SP};
use syntax::parse::token;
use syntax::ptr::P;
use syntax::visit;
Expand All @@ -33,7 +34,7 @@ pub type PluginRegistrarFun =

pub struct PluginRegistrar {
pub fun: PluginRegistrarFun,
pub args: P<ast::MetaItem>,
pub args: Vec<P<ast::MetaItem>>,
}

/// Information about loaded plugins.
Expand Down Expand Up @@ -81,10 +82,34 @@ pub fn load_plugins(sess: &Session, krate: &ast::Crate,

visit::walk_crate(&mut loader, krate);

for attr in &krate.attrs {
if !attr.check_name("plugin") {
continue;
}

let plugins = match attr.meta_item_list() {
Some(xs) => xs,
None => {
sess.span_err(attr.span, "malformed plugin attribute");
continue;
}
};

for plugin in plugins {
if plugin.value_str().is_some() {
sess.span_err(attr.span, "malformed plugin attribute");
continue;
}

let args = plugin.meta_item_list().map(ToOwned::to_owned).unwrap_or_default();
loader.load_plugin(CrateOrString::Str(plugin.span, &*plugin.name()),
args);
}
}

if let Some(plugins) = addl_plugins {
for plugin in plugins {
loader.load_plugin(CrateOrString::Str(&plugin),
None, None, None)
loader.load_plugin(CrateOrString::Str(COMMAND_LINE_SP, &plugin), vec![]);
}
}

Expand All @@ -104,21 +129,16 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
}

// Parse the attributes relating to macro / plugin loading.
let mut plugin_attr = None;
let mut macro_selection = Some(HashSet::new()); // None => load all
let mut reexport = HashSet::new();
for attr in &item.attrs {
let mut used = true;
match &attr.name()[] {
"phase" => {
self.sess.span_err(attr.span, "#[phase] is deprecated; use \
#[macro_use], #[plugin], and/or #[no_link]");
self.sess.span_err(attr.span, "#[phase] is deprecated");
}
"plugin" => {
if plugin_attr.is_some() {
self.sess.span_err(attr.span, "#[plugin] specified multiple times");
}
plugin_attr = Some(attr.node.value.clone());
self.sess.span_err(attr.span, "#[plugin] on `extern crate` is deprecated");
}
"macro_use" => {
let names = attr.meta_item_list();
Expand Down Expand Up @@ -160,10 +180,7 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
}
}

self.load_plugin(CrateOrString::Krate(item),
plugin_attr,
macro_selection,
Some(reexport))
self.load_macros(item, macro_selection, Some(reexport))
}

fn visit_mac(&mut self, _: &ast::Mac) {
Expand All @@ -173,38 +190,25 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
}

impl<'a> PluginLoader<'a> {
pub fn load_plugin<'b>(&mut self,
c: CrateOrString<'b>,
plugin_attr: Option<P<ast::MetaItem>>,
pub fn load_macros<'b>(&mut self,
vi: &ast::Item,
macro_selection: Option<HashSet<token::InternedString>>,
reexport: Option<HashSet<token::InternedString>>) {
let mut macros = vec![];
let mut registrar = None;

let load_macros = match (macro_selection.as_ref(), reexport.as_ref()) {
(Some(sel), Some(re)) => sel.len() != 0 || re.len() != 0,
_ => true,
};
let load_registrar = plugin_attr.is_some();

if let CrateOrString::Krate(vi) = c {
if load_macros && !self.span_whitelist.contains(&vi.span) {
self.sess.span_err(vi.span, "an `extern crate` loading macros must be at \
the crate root");
if let (Some(sel), Some(re)) = (macro_selection.as_ref(), reexport.as_ref()) {
if sel.is_empty() && re.is_empty() {
return;
}
}
}

if load_macros || load_registrar {
let pmd = self.reader.read_plugin_metadata(c);
if load_macros {
macros = pmd.exported_macros();
}
if load_registrar {
registrar = pmd.plugin_registrar();
}
if !self.span_whitelist.contains(&vi.span) {
self.sess.span_err(vi.span, "an `extern crate` loading macros must be at \
the crate root");
return;
}

for mut def in macros {
let pmd = self.reader.read_plugin_metadata(CrateOrString::Krate(vi));

for mut def in pmd.exported_macros() {
let name = token::get_ident(def.ident);
def.use_locally = match macro_selection.as_ref() {
None => true,
Expand All @@ -217,12 +221,21 @@ impl<'a> PluginLoader<'a> {
};
self.plugins.macros.push(def);
}
}

pub fn load_plugin<'b>(&mut self,
c: CrateOrString<'b>,
args: Vec<P<ast::MetaItem>>) {
let registrar = {
let pmd = self.reader.read_plugin_metadata(c);
pmd.plugin_registrar()
};

if let Some((lib, symbol)) = registrar {
let fun = self.dylink_registrar(c, lib, symbol);
self.plugins.registrars.push(PluginRegistrar {
fun: fun,
args: plugin_attr.unwrap(),
args: args,
});
}
}
Expand Down
7 changes: 1 addition & 6 deletions src/librustc/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,9 @@
//!
//! ```rust
//! #![feature(plugin)]
//!
//! #[plugin]
//! extern crate myplugin;
//! #![plugin(myplugin)]
//! ```
//!
//! If you don't need the plugin crate available at runtime, use
//! `#[no_link]` as well.
//!
//! See [the compiler plugin guide](../../guide-plugin.html)
//! for more examples.

Expand Down
13 changes: 8 additions & 5 deletions src/librustc/plugin/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub struct Registry<'a> {
pub sess: &'a Session,

#[doc(hidden)]
pub args_hidden: Option<P<ast::MetaItem>>,
pub args_hidden: Option<Vec<P<ast::MetaItem>>>,

#[doc(hidden)]
pub krate_span: Span,
Expand Down Expand Up @@ -65,11 +65,14 @@ impl<'a> Registry<'a> {
}
}

/// Get the `#[plugin]` attribute used to load this plugin.
/// Get the plugin's arguments, if any.
///
/// This gives access to arguments passed via `#[plugin=...]` or
/// `#[plugin(...)]`.
pub fn args<'b>(&'b self) -> &'b P<ast::MetaItem> {
/// These are specified inside the `plugin` crate attribute as
///
/// ```no_run
/// #![plugin(my_plugin_name(... args ...))]
/// ```
pub fn args<'b>(&'b self) -> &'b Vec<P<ast::MetaItem>> {
self.args_hidden.as_ref().expect("args not set")
}

Expand Down
1 change: 0 additions & 1 deletion src/librustc_driver/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ use rustc::session::config::{Input, PrintRequest, UnstableFeatures};
use rustc::lint::Lint;
use rustc::lint;
use rustc::metadata;
use rustc::metadata::creader::CrateOrString::Str;
use rustc::util::common::time;

use std::cmp::Ordering::Equal;
Expand Down
10 changes: 5 additions & 5 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
}
match i.node {
ast::ItemExternCrate(_) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a message telling people what to do if they still have a #[plugin]-tagged extern crate?

if attr::contains_name(&i.attrs[], "plugin") {
self.gate_feature("plugin", i.span,
"compiler plugins are experimental \
and possibly buggy");
} else if attr::contains_name(&i.attrs[], "macro_reexport") {
if attr::contains_name(&i.attrs[], "macro_reexport") {
self.gate_feature("macro_reexport", i.span,
"macros reexports are experimental \
and possibly buggy");
Expand Down Expand Up @@ -462,6 +458,10 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
if attr.check_name("staged_api") {
self.gate_feature("staged_api", attr.span,
"staged_api is for use by rustc only");
} else if attr.check_name("plugin") {
self.gate_feature("plugin", attr.span,
"compiler plugins are experimental \
and possibly buggy");
}

if attr::contains_name(slice::ref_slice(attr), "lang") {
Expand Down
9 changes: 4 additions & 5 deletions src/test/auxiliary/plugin_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,17 @@ use syntax::ptr::P;
use rustc::plugin::Registry;

struct Expander {
args: P<ast::MetaItem>,
args: Vec<P<ast::MetaItem>>,
}

impl TTMacroExpander for Expander {
fn expand<'cx>(&self,
ecx: &'cx mut ExtCtxt,
sp: Span,
_: &[ast::TokenTree]) -> Box<MacResult+'cx> {

let attr = ecx.attribute(sp, self.args.clone());
let src = pprust::attribute_to_string(&attr);
let interned = token::intern_and_get_ident(&src);
let args = self.args.iter().map(|i| pprust::meta_item_to_string(&*i))
.collect::<Vec<_>>().connect(", ");
let interned = token::intern_and_get_ident(&args[]);
MacExpr::new(ecx.expr_str(sp, interned))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#![crate_type = "dylib"]
#![feature(plugin_registrar, quote)]

extern crate "syntax-extension-with-dll-deps-1" as other;
extern crate "syntax_extension_with_dll_deps_1" as other;
extern crate syntax;
extern crate rustc;

Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail-fulldeps/gated-plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// aux-build:macro_crate_test.rs
// ignore-stage1

#[plugin] #[no_link] extern crate macro_crate_test;
#![plugin(macro_crate_test)]
//~^ ERROR compiler plugins are experimental and possibly buggy

fn main() {}
Loading