about summary refs log tree commit diff
path: root/src/librustc_plugin_impl
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2019-11-17 00:54:24 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2019-11-17 11:04:49 +0300
commit7f49f7bcc275062ac7f77b6f58d72e8b180bf367 (patch)
tree86343fde846cea1f95795d92c445ba2e609f412d /src/librustc_plugin_impl
parentce7a579cace5de621071a9e18416268e93246fdd (diff)
downloadrust-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.toml19
-rw-r--r--src/librustc_plugin_impl/build.rs68
-rw-r--r--src/librustc_plugin_impl/lib.rs64
-rw-r--r--src/librustc_plugin_impl/load.rs137
-rw-r--r--src/librustc_plugin_impl/registry.rs96
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());
+    }
+}