diff options
| author | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-11-17 00:54:24 +0300 |
|---|---|---|
| committer | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-11-17 11:04:49 +0300 |
| commit | 7f49f7bcc275062ac7f77b6f58d72e8b180bf367 (patch) | |
| tree | 86343fde846cea1f95795d92c445ba2e609f412d /src/librustc_plugin_impl | |
| parent | ce7a579cace5de621071a9e18416268e93246fdd (diff) | |
| download | rust-7f49f7bcc275062ac7f77b6f58d72e8b180bf367.tar.gz rust-7f49f7bcc275062ac7f77b6f58d72e8b180bf367.zip | |
Rename directory `rustc_plugin` -> `rustc_plugin_impl`
Diffstat (limited to 'src/librustc_plugin_impl')
| -rw-r--r-- | src/librustc_plugin_impl/Cargo.toml | 19 | ||||
| -rw-r--r-- | src/librustc_plugin_impl/build.rs | 68 | ||||
| -rw-r--r-- | src/librustc_plugin_impl/lib.rs | 64 | ||||
| -rw-r--r-- | src/librustc_plugin_impl/load.rs | 137 | ||||
| -rw-r--r-- | src/librustc_plugin_impl/registry.rs | 96 |
5 files changed, 384 insertions, 0 deletions
diff --git a/src/librustc_plugin_impl/Cargo.toml b/src/librustc_plugin_impl/Cargo.toml new file mode 100644 index 00000000000..c57b32fb2b6 --- /dev/null +++ b/src/librustc_plugin_impl/Cargo.toml @@ -0,0 +1,19 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_plugin_impl" +version = "0.0.0" +build = false +edition = "2018" + +[lib] +name = "rustc_plugin_impl" +path = "lib.rs" +doctest = false + +[dependencies] +rustc = { path = "../librustc" } +rustc_metadata = { path = "../librustc_metadata" } +syntax = { path = "../libsyntax" } +syntax_expand = { path = "../libsyntax_expand" } +syntax_pos = { path = "../libsyntax_pos" } +rustc_error_codes = { path = "../librustc_error_codes" } diff --git a/src/librustc_plugin_impl/build.rs b/src/librustc_plugin_impl/build.rs new file mode 100644 index 00000000000..01559a95c9c --- /dev/null +++ b/src/librustc_plugin_impl/build.rs @@ -0,0 +1,68 @@ +//! Used by `rustc` when compiling a plugin crate. + +use syntax::attr; +use syntax::symbol::sym; +use syntax_pos::Span; +use rustc::hir::itemlikevisit::ItemLikeVisitor; +use rustc::hir; +use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc::ty::TyCtxt; +use rustc::ty::query::Providers; + +struct RegistrarFinder { + registrars: Vec<(hir::HirId, Span)> , +} + +impl<'v> ItemLikeVisitor<'v> for RegistrarFinder { + fn visit_item(&mut self, item: &hir::Item) { + if let hir::ItemKind::Fn(..) = item.kind { + if attr::contains_name(&item.attrs, sym::plugin_registrar) { + self.registrars.push((item.hir_id, item.span)); + } + } + } + + fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) { + } + + fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) { + } +} + +/// Finds the function marked with `#[plugin_registrar]`, if any. +pub fn find_plugin_registrar(tcx: TyCtxt<'_>) -> Option<DefId> { + tcx.plugin_registrar_fn(LOCAL_CRATE) +} + +fn plugin_registrar_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<DefId> { + assert_eq!(cnum, LOCAL_CRATE); + + let mut finder = RegistrarFinder { registrars: Vec::new() }; + tcx.hir().krate().visit_all_item_likes(&mut finder); + + match finder.registrars.len() { + 0 => None, + 1 => { + let (hir_id, _) = finder.registrars.pop().unwrap(); + Some(tcx.hir().local_def_id(hir_id)) + }, + _ => { + let diagnostic = tcx.sess.diagnostic(); + let mut e = diagnostic.struct_err("multiple plugin registration functions found"); + for &(_, span) in &finder.registrars { + e.span_note(span, "one is here"); + } + e.emit(); + diagnostic.abort_if_errors(); + unreachable!(); + } + } +} + + +pub fn provide(providers: &mut Providers<'_>) { + *providers = Providers { + plugin_registrar_fn, + ..*providers + }; +} diff --git a/src/librustc_plugin_impl/lib.rs b/src/librustc_plugin_impl/lib.rs new file mode 100644 index 00000000000..83e9ebeb935 --- /dev/null +++ b/src/librustc_plugin_impl/lib.rs @@ -0,0 +1,64 @@ +//! 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: +//! +//! ```no_run +//! #![crate_name = "myplugin"] +//! #![crate_type = "dylib"] +//! #![feature(plugin_registrar)] +//! #![feature(rustc_private)] +//! +//! extern crate rustc_driver; +//! extern crate syntax; +//! extern crate syntax_pos; +//! +//! use rustc_driver::plugin::Registry; +//! use syntax_expand::base::{ExtCtxt, MacResult}; +//! use syntax_pos::Span; +//! use syntax::tokenstream::TokenTree; +//! +//! #[plugin_registrar] +//! pub fn plugin_registrar(reg: &mut Registry) { +//! reg.register_macro("mymacro", expand_mymacro); +//! } +//! +//! fn expand_mymacro(cx: &mut ExtCtxt, span: Span, tt: &[TokenTree]) -> Box<MacResult> { +//! unimplemented!() +//! } +//! +//! # fn main() {} +//! ``` +//! +//! 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 [`plugin` +//! feature](https://doc.rust-lang.org/nightly/unstable-book/language-features/plugin.html) +//! of the Unstable Book for more examples. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] + +#![feature(nll)] + +#![recursion_limit="256"] + +pub use registry::Registry; + +pub mod registry; +pub mod load; +pub mod build; diff --git a/src/librustc_plugin_impl/load.rs b/src/librustc_plugin_impl/load.rs new file mode 100644 index 00000000000..31b3b07c3e1 --- /dev/null +++ b/src/librustc_plugin_impl/load.rs @@ -0,0 +1,137 @@ +//! Used by `rustc` when loading a plugin. + +use rustc::middle::cstore::MetadataLoader; +use rustc::session::Session; +use rustc_metadata::locator; +use crate::registry::Registry; + +use std::borrow::ToOwned; +use std::env; +use std::mem; +use std::path::PathBuf; +use syntax::ast; +use syntax::struct_span_err; +use syntax::symbol::{Symbol, kw, sym}; +use syntax_pos::{Span, DUMMY_SP}; + +use rustc_error_codes::*; + +/// Pointer to a registrar function. +pub type PluginRegistrarFun = + fn(&mut Registry<'_>); + +pub struct PluginRegistrar { + pub fun: PluginRegistrarFun, + pub args: Vec<ast::NestedMetaItem>, +} + +struct PluginLoader<'a> { + sess: &'a Session, + metadata_loader: &'a dyn MetadataLoader, + plugins: Vec<PluginRegistrar>, +} + +fn call_malformed_plugin_attribute(sess: &Session, span: Span) { + struct_span_err!(sess, span, E0498, "malformed `plugin` attribute") + .span_label(span, "malformed attribute") + .emit(); +} + +/// Read plugin metadata and dynamically load registrar functions. +pub fn load_plugins(sess: &Session, + metadata_loader: &dyn MetadataLoader, + krate: &ast::Crate, + addl_plugins: Option<Vec<String>>) -> Vec<PluginRegistrar> { + let mut loader = PluginLoader { sess, metadata_loader, plugins: Vec::new() }; + + // do not report any error now. since crate attributes are + // not touched by expansion, every use of plugin without + // the feature enabled will result in an error later... + if sess.features_untracked().plugin { + for attr in &krate.attrs { + if !attr.check_name(sym::plugin) { + continue; + } + + let plugins = match attr.meta_item_list() { + Some(xs) => xs, + None => continue, + }; + + for plugin in plugins { + // plugins must have a name and can't be key = value + let name = plugin.name_or_empty(); + if name != kw::Invalid && !plugin.is_value_str() { + let args = plugin.meta_item_list().map(ToOwned::to_owned); + loader.load_plugin(plugin.span(), name, args.unwrap_or_default()); + } else { + call_malformed_plugin_attribute(sess, attr.span); + } + } + } + } + + if let Some(plugins) = addl_plugins { + for plugin in plugins { + loader.load_plugin(DUMMY_SP, Symbol::intern(&plugin), vec![]); + } + } + + loader.plugins +} + +impl<'a> PluginLoader<'a> { + fn load_plugin(&mut self, span: Span, name: Symbol, args: Vec<ast::NestedMetaItem>) { + let registrar = locator::find_plugin_registrar(self.sess, self.metadata_loader, span, name); + + if let Some((lib, disambiguator)) = registrar { + let symbol = self.sess.generate_plugin_registrar_symbol(disambiguator); + let fun = self.dylink_registrar(span, lib, symbol); + self.plugins.push(PluginRegistrar { + fun, + args, + }); + } + } + + // Dynamically link a registrar function into the compiler process. + fn dylink_registrar(&mut self, + span: Span, + path: PathBuf, + symbol: String) -> PluginRegistrarFun { + use rustc_metadata::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_impl/registry.rs b/src/librustc_plugin_impl/registry.rs new file mode 100644 index 00000000000..aa5ea80f0b0 --- /dev/null +++ b/src/librustc_plugin_impl/registry.rs @@ -0,0 +1,96 @@ +//! Used by plugin crates to tell `rustc` about the plugins they provide. + +use rustc::lint::LintStore; +use rustc::session::Session; + +use syntax_expand::base::{SyntaxExtension, SyntaxExtensionKind, NamedSyntaxExtension}; +use syntax_expand::base::MacroExpanderFn; +use syntax::symbol::Symbol; +use syntax::ast; +use syntax_pos::Span; + +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, + + /// The `LintStore` allows plugins to register new lints. + pub lint_store: &'a mut LintStore, + + #[doc(hidden)] + pub args_hidden: Option<Vec<ast::NestedMetaItem>>, + + #[doc(hidden)] + pub krate_span: Span, + + #[doc(hidden)] + pub syntax_exts: Vec<NamedSyntaxExtension>, + + #[doc(hidden)] + pub llvm_passes: Vec<String>, +} + +impl<'a> Registry<'a> { + #[doc(hidden)] + pub fn new(sess: &'a Session, lint_store: &'a mut LintStore, krate_span: Span) -> Registry<'a> { + Registry { + sess, + lint_store, + args_hidden: None, + krate_span, + syntax_exts: vec![], + llvm_passes: vec![], + } + } + + /// Gets the plugin's arguments, if any. + /// + /// These are specified inside the `plugin` crate attribute as + /// + /// ```no_run + /// #![plugin(my_plugin_name(... args ...))] + /// ``` + /// + /// Returns empty slice in case the plugin was loaded + /// with `--extra-plugins` + pub fn args(&self) -> &[ast::NestedMetaItem] { + self.args_hidden.as_ref().map(|v| &v[..]).unwrap_or(&[]) + } + + /// 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, extension)); + } + + /// Register a macro of the usual kind. + /// + /// This is a convenience wrapper for `register_syntax_extension`. + /// It builds for you a `SyntaxExtensionKind::LegacyBang` that calls `expander`, + /// and also takes care of interning the macro's name. + pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) { + let kind = SyntaxExtensionKind::LegacyBang(Box::new(expander)); + let ext = SyntaxExtension::default(kind, self.sess.edition()); + self.register_syntax_extension(Symbol::intern(name), ext); + } + + /// 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()); + } +} |
