diff options
| author | Ariel Ben-Yehuda <ariel.byd@gmail.com> | 2015-11-22 22:14:09 +0200 |
|---|---|---|
| committer | Ariel Ben-Yehuda <arielb1@mail.tau.ac.il> | 2015-11-26 18:22:39 +0200 |
| commit | 1430a3500076ad504a0b30be77fd2ad4468ea769 (patch) | |
| tree | 61739ffce74c80568a634852a4b09b5ec689d365 /src/librustc_plugin | |
| parent | 26b19206d34f578a8eae658ece59990d8abb43ba (diff) | |
| download | rust-1430a3500076ad504a0b30be77fd2ad4468ea769.tar.gz rust-1430a3500076ad504a0b30be77fd2ad4468ea769.zip | |
move librustc/plugin to librustc_plugin
this is a [breaking-change] to all plugin authors - sorry
Diffstat (limited to 'src/librustc_plugin')
| -rw-r--r-- | src/librustc_plugin/build.rs | 57 | ||||
| -rw-r--r-- | src/librustc_plugin/diagnostics.rs | 19 | ||||
| -rw-r--r-- | src/librustc_plugin/lib.rs | 80 | ||||
| -rw-r--r-- | src/librustc_plugin/load.rs | 146 | ||||
| -rw-r--r-- | src/librustc_plugin/registry.rs | 155 |
5 files changed, 457 insertions, 0 deletions
diff --git a/src/librustc_plugin/build.rs b/src/librustc_plugin/build.rs new file mode 100644 index 00000000000..00f58c6af91 --- /dev/null +++ b/src/librustc_plugin/build.rs @@ -0,0 +1,57 @@ +// 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. + +//! Used by `rustc` when compiling a plugin crate. + +use syntax::ast; +use syntax::attr; +use syntax::codemap::Span; +use syntax::diagnostic; +use rustc_front::intravisit::Visitor; +use rustc_front::hir; + +struct RegistrarFinder { + registrars: Vec<(ast::NodeId, Span)> , +} + +impl<'v> Visitor<'v> for RegistrarFinder { + fn visit_item(&mut self, item: &hir::Item) { + if let hir::ItemFn(..) = item.node { + if attr::contains_name(&item.attrs, + "plugin_registrar") { + self.registrars.push((item.id, item.span)); + } + } + } +} + +/// Find the function marked with `#[plugin_registrar]`, if any. +pub fn find_plugin_registrar(diagnostic: &diagnostic::SpanHandler, + krate: &hir::Crate) + -> Option<ast::NodeId> { + let mut finder = RegistrarFinder { registrars: Vec::new() }; + krate.visit_all_items(&mut finder); + + match finder.registrars.len() { + 0 => None, + 1 => { + let (node_id, _) = finder.registrars.pop().unwrap(); + Some(node_id) + }, + _ => { + diagnostic.handler().err("multiple plugin registration functions found"); + for &(_, span) in &finder.registrars { + diagnostic.span_note(span, "one is here"); + } + diagnostic.handler().abort_if_errors(); + unreachable!(); + } + } +} diff --git a/src/librustc_plugin/diagnostics.rs b/src/librustc_plugin/diagnostics.rs new file mode 100644 index 00000000000..100c1db1439 --- /dev/null +++ b/src/librustc_plugin/diagnostics.rs @@ -0,0 +1,19 @@ +// 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 <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. + +#![allow(non_snake_case)] + +register_long_diagnostics! { + +} + +register_diagnostics! { + E0498 // malformed plugin attribute +} diff --git a/src/librustc_plugin/lib.rs b/src/librustc_plugin/lib.rs new file mode 100644 index 00000000000..33d63d833c7 --- /dev/null +++ b/src/librustc_plugin/lib.rs @@ -0,0 +1,80 @@ +// 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. + +//! Infrastructure for compiler plugins. +//! +//! Plugins are Rust libraries which extend the behavior of `rustc` +//! in various ways. +//! +//! Plugin authors will use the `Registry` type re-exported by +//! this module, along with its methods. The rest of the module +//! is for use by `rustc` itself. +//! +//! To define a plugin, build a dylib crate with a +//! `#[plugin_registrar]` function: +//! +//! ```rust,ignore +//! #![crate_name = "myplugin"] +//! #![crate_type = "dylib"] +//! #![feature(plugin_registrar)] +//! +//! extern crate rustc; +//! +//! use rustc::plugin::Registry; +//! +//! #[plugin_registrar] +//! pub fn plugin_registrar(reg: &mut Registry) { +//! reg.register_macro("mymacro", expand_mymacro); +//! } +//! +//! fn expand_mymacro(...) { // details elided +//! ``` +//! +//! WARNING: We currently don't check that the registrar function +//! has the appropriate type! +//! +//! To use a plugin while compiling another crate: +//! +//! ```rust +//! #![feature(plugin)] +//! #![plugin(myplugin)] +//! ``` +//! +//! See the [Plugins Chapter](../../book/compiler-plugins.html) of the book +//! for more examples. + +#![cfg_attr(stage0, feature(custom_attribute))] +#![crate_name = "rustc_plugin"] +#![unstable(feature = "rustc_private", issue = "27812")] +#![cfg_attr(stage0, staged_api)] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] + +#![feature(dynamic_lib)] +#![feature(staged_api)] +#![feature(rustc_diagnostic_macros)] +#![feature(rustc_private)] + +#[macro_use] extern crate log; +#[macro_use] extern crate syntax; +#[macro_use] #[no_link] extern crate rustc_bitflags; + +extern crate rustc; +extern crate rustc_front; + +pub use self::registry::Registry; + +pub mod diagnostics; +pub mod registry; +pub mod load; +pub mod build; diff --git a/src/librustc_plugin/load.rs b/src/librustc_plugin/load.rs new file mode 100644 index 00000000000..d121a306c61 --- /dev/null +++ b/src/librustc_plugin/load.rs @@ -0,0 +1,146 @@ +// 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. + +//! Used by `rustc` when loading a plugin. + +use rustc::session::Session; +use rustc::metadata::creader::CrateReader; +use rustc::metadata::cstore::CStore; +use registry::Registry; + +use std::borrow::ToOwned; +use std::env; +use std::mem; +use std::path::PathBuf; +use syntax::ast; +use syntax::codemap::{Span, COMMAND_LINE_SP}; +use syntax::ptr::P; +use syntax::attr::AttrMetaMethods; + +/// Pointer to a registrar function. +pub type PluginRegistrarFun = + fn(&mut Registry); + +pub struct PluginRegistrar { + pub fun: PluginRegistrarFun, + pub args: Vec<P<ast::MetaItem>>, +} + +struct PluginLoader<'a> { + sess: &'a Session, + reader: CrateReader<'a>, + plugins: Vec<PluginRegistrar>, +} + +fn call_malformed_plugin_attribute(a: &Session, b: Span) { + span_err!(a, b, E0498, "malformed plugin attribute"); +} + +/// Read plugin metadata and dynamically load registrar functions. +pub fn load_plugins(sess: &Session, cstore: &CStore, krate: &ast::Crate, + addl_plugins: Option<Vec<String>>) -> Vec<PluginRegistrar> { + let mut loader = PluginLoader::new(sess, cstore); + + for attr in &krate.attrs { + if !attr.check_name("plugin") { + continue; + } + + let plugins = match attr.meta_item_list() { + Some(xs) => xs, + None => { + call_malformed_plugin_attribute(sess, attr.span); + continue; + } + }; + + for plugin in plugins { + if plugin.value_str().is_some() { + call_malformed_plugin_attribute(sess, attr.span); + continue; + } + + let args = plugin.meta_item_list().map(ToOwned::to_owned).unwrap_or_default(); + loader.load_plugin(plugin.span, &*plugin.name(), args); + } + } + + if let Some(plugins) = addl_plugins { + for plugin in plugins { + loader.load_plugin(COMMAND_LINE_SP, &plugin, vec![]); + } + } + + loader.plugins +} + +impl<'a> PluginLoader<'a> { + fn new(sess: &'a Session, cstore: &'a CStore) -> PluginLoader<'a> { + PluginLoader { + sess: sess, + reader: CrateReader::new(sess, cstore), + plugins: vec![], + } + } + + fn load_plugin(&mut self, span: Span, name: &str, args: Vec<P<ast::MetaItem>>) { + let registrar = self.reader.find_plugin_registrar(span, name); + + if let Some((lib, symbol)) = registrar { + let fun = self.dylink_registrar(span, lib, symbol); + self.plugins.push(PluginRegistrar { + fun: fun, + args: args, + }); + } + } + + // Dynamically link a registrar function into the compiler process. + #[allow(deprecated)] + fn dylink_registrar(&mut self, + span: Span, + path: PathBuf, + symbol: String) -> PluginRegistrarFun { + use std::dynamic_lib::DynamicLibrary; + + // Make sure the path contains a / or the linker will search for it. + let path = env::current_dir().unwrap().join(&path); + + let lib = match DynamicLibrary::open(Some(&path)) { + Ok(lib) => lib, + // this is fatal: there are almost certainly macros we need + // inside this crate, so continue would spew "macro undefined" + // errors + Err(err) => { + self.sess.span_fatal(span, &err[..]) + } + }; + + unsafe { + let registrar = + match lib.symbol(&symbol[..]) { + Ok(registrar) => { + mem::transmute::<*mut u8,PluginRegistrarFun>(registrar) + } + // again fatal if we can't register macros + Err(err) => { + self.sess.span_fatal(span, &err[..]) + } + }; + + // 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 thread). + mem::forget(lib); + + registrar + } + } +} diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs new file mode 100644 index 00000000000..3138d7fa1db --- /dev/null +++ b/src/librustc_plugin/registry.rs @@ -0,0 +1,155 @@ +// 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. + +//! Used by plugin crates to tell `rustc` about the plugins they provide. + +use rustc::lint::{EarlyLintPassObject, LateLintPassObject, LintId, Lint}; +use rustc::session::Session; + +use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT}; +use syntax::ext::base::{IdentTT, MultiModifier, MultiDecorator}; +use syntax::ext::base::{MacroExpanderFn, MacroRulesTT}; +use syntax::codemap::Span; +use syntax::parse::token; +use syntax::ptr::P; +use syntax::ast; +use syntax::feature_gate::AttributeType; + +use std::collections::HashMap; +use std::borrow::ToOwned; + +/// Structure used to register plugins. +/// +/// A plugin registrar function takes an `&mut Registry` and should call +/// methods to register its plugins. +/// +/// This struct has public fields and other methods for use by `rustc` +/// itself. They are not documented here, and plugin authors should +/// not use them. +pub struct Registry<'a> { + /// Compiler session. Useful if you want to emit diagnostic messages + /// from the plugin registrar. + pub sess: &'a Session, + + #[doc(hidden)] + pub args_hidden: Option<Vec<P<ast::MetaItem>>>, + + #[doc(hidden)] + pub krate_span: Span, + + #[doc(hidden)] + pub syntax_exts: Vec<NamedSyntaxExtension>, + + #[doc(hidden)] + pub early_lint_passes: Vec<EarlyLintPassObject>, + + #[doc(hidden)] + pub late_lint_passes: Vec<LateLintPassObject>, + + #[doc(hidden)] + pub lint_groups: HashMap<&'static str, Vec<LintId>>, + + #[doc(hidden)] + pub llvm_passes: Vec<String>, + + #[doc(hidden)] + pub attributes: Vec<(String, AttributeType)>, +} + +impl<'a> Registry<'a> { + #[doc(hidden)] + pub fn new(sess: &'a Session, krate: &ast::Crate) -> Registry<'a> { + Registry { + sess: sess, + args_hidden: None, + krate_span: krate.span, + syntax_exts: vec!(), + early_lint_passes: vec!(), + late_lint_passes: vec!(), + lint_groups: HashMap::new(), + llvm_passes: vec!(), + attributes: vec!(), + } + } + + /// Get the plugin's arguments, if any. + /// + /// 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") + } + + /// Register a syntax extension of any kind. + /// + /// This is the most general hook into `libsyntax`'s expansion behavior. + pub fn register_syntax_extension(&mut self, name: ast::Name, extension: SyntaxExtension) { + self.syntax_exts.push((name, match extension { + NormalTT(ext, _, allow_internal_unstable) => { + NormalTT(ext, Some(self.krate_span), allow_internal_unstable) + } + IdentTT(ext, _, allow_internal_unstable) => { + IdentTT(ext, Some(self.krate_span), allow_internal_unstable) + } + MultiDecorator(ext) => MultiDecorator(ext), + MultiModifier(ext) => MultiModifier(ext), + MacroRulesTT => { + self.sess.err("plugin tried to register a new MacroRulesTT"); + return; + } + })); + } + + /// Register a macro of the usual kind. + /// + /// This is a convenience wrapper for `register_syntax_extension`. + /// It builds for you a `NormalTT` that calls `expander`, + /// and also takes care of interning the macro's name. + pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) { + self.register_syntax_extension(token::intern(name), + NormalTT(Box::new(expander), None, false)); + } + + /// Register a compiler lint pass. + pub fn register_early_lint_pass(&mut self, lint_pass: EarlyLintPassObject) { + self.early_lint_passes.push(lint_pass); + } + + /// Register a compiler lint pass. + pub fn register_late_lint_pass(&mut self, lint_pass: LateLintPassObject) { + self.late_lint_passes.push(lint_pass); + } + /// Register a lint group. + pub fn register_lint_group(&mut self, name: &'static str, to: Vec<&'static Lint>) { + self.lint_groups.insert(name, to.into_iter().map(|x| LintId::of(x)).collect()); + } + + /// Register an LLVM pass. + /// + /// Registration with LLVM itself is handled through static C++ objects with + /// constructors. This method simply adds a name to the list of passes to + /// execute. + pub fn register_llvm_pass(&mut self, name: &str) { + self.llvm_passes.push(name.to_owned()); + } + + + /// Register an attribute with an attribute type. + /// + /// Registered attributes will bypass the `custom_attribute` feature gate. + /// `Whitelisted` attributes will additionally not trigger the `unused_attribute` + /// lint. `CrateLevel` attributes will not be allowed on anything other than a crate. + pub fn register_attribute(&mut self, name: String, ty: AttributeType) { + self.attributes.push((name, ty)); + } +} |
