From 6864792df002a4430634ea6ae096dd7e524560c2 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Wed, 11 Feb 2015 19:53:10 -0800 Subject: [PATCH] Separate macro and plugin loading Now they just share a bit of code internal to creader. Resolves #22198 to my satisfaction. --- src/librustc/metadata/macro_import.rs | 186 ++++++++++++++++++++++++++ src/librustc/metadata/mod.rs | 1 + src/librustc/plugin/load.rs | 179 ++----------------------- src/librustc_driver/driver.rs | 10 +- 4 files changed, 205 insertions(+), 171 deletions(-) create mode 100644 src/librustc/metadata/macro_import.rs diff --git a/src/librustc/metadata/macro_import.rs b/src/librustc/metadata/macro_import.rs new file mode 100644 index 00000000000..28c98d455f0 --- /dev/null +++ b/src/librustc/metadata/macro_import.rs @@ -0,0 +1,186 @@ +// Copyright 2012-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. + +//! Used by `rustc` when loading a crate with exported macros. + +use session::Session; +use metadata::creader::CrateReader; + +use std::collections::{HashSet, HashMap}; +use syntax::ast; +use syntax::attr; +use syntax::codemap::Span; +use syntax::parse::token; +use syntax::visit; +use syntax::visit::Visitor; +use syntax::attr::AttrMetaMethods; + +struct MacroLoader<'a> { + sess: &'a Session, + span_whitelist: HashSet, + reader: CrateReader<'a>, + macros: Vec, +} + +impl<'a> MacroLoader<'a> { + fn new(sess: &'a Session) -> MacroLoader<'a> { + MacroLoader { + sess: sess, + span_whitelist: HashSet::new(), + reader: CrateReader::new(sess), + macros: vec![], + } + } +} + +/// Read exported macros. +pub fn read_macro_defs(sess: &Session, krate: &ast::Crate) -> Vec { + let mut loader = MacroLoader::new(sess); + + // We need to error on `#[macro_use] extern crate` when it isn't at the + // crate root, because `$crate` won't work properly. Identify these by + // spans, because the crate map isn't set up yet. + for item in &krate.module.items { + if let ast::ItemExternCrate(_) = item.node { + loader.span_whitelist.insert(item.span); + } + } + + visit::walk_crate(&mut loader, krate); + + loader.macros +} + +pub type MacroSelection = HashMap; + +// note that macros aren't expanded yet, and therefore macros can't add macro imports. +impl<'a, 'v> Visitor<'v> for MacroLoader<'a> { + fn visit_item(&mut self, item: &ast::Item) { + // We're only interested in `extern crate`. + match item.node { + ast::ItemExternCrate(_) => {} + _ => { + visit::walk_item(self, item); + return; + } + } + + // Parse the attributes relating to macros. + let mut import = Some(HashMap::new()); // None => load all + let mut reexport = HashMap::new(); + + for attr in &item.attrs { + let mut used = true; + match &attr.name()[] { + "phase" => { + self.sess.span_err(attr.span, "#[phase] is deprecated"); + } + "plugin" => { + self.sess.span_err(attr.span, "#[plugin] on `extern crate` is deprecated"); + self.sess.span_help(attr.span, &format!("use a crate attribute instead, \ + i.e. #![plugin({})]", + item.ident.as_str())[]); + } + "macro_use" => { + let names = attr.meta_item_list(); + if names.is_none() { + // no names => load all + import = None; + } + if let (Some(sel), Some(names)) = (import.as_mut(), names) { + for attr in names { + if let ast::MetaWord(ref name) = attr.node { + sel.insert(name.clone(), attr.span); + } else { + self.sess.span_err(attr.span, "bad macro import"); + } + } + } + } + "macro_reexport" => { + let names = match attr.meta_item_list() { + Some(names) => names, + None => { + self.sess.span_err(attr.span, "bad macro reexport"); + continue; + } + }; + + for attr in names { + if let ast::MetaWord(ref name) = attr.node { + reexport.insert(name.clone(), attr.span); + } else { + self.sess.span_err(attr.span, "bad macro reexport"); + } + } + } + _ => used = false, + } + if used { + attr::mark_used(attr); + } + } + + self.load_macros(item, import, reexport) + } + + fn visit_mac(&mut self, _: &ast::Mac) { + // bummer... can't see macro imports inside macros. + // do nothing. + } +} + +impl<'a> MacroLoader<'a> { + fn load_macros<'b>(&mut self, + vi: &ast::Item, + import: Option, + reexport: MacroSelection) { + if let Some(sel) = import.as_ref() { + if sel.is_empty() && reexport.is_empty() { + return; + } + } + + 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; + } + + let macros = self.reader.read_exported_macros(vi); + let mut seen = HashSet::new(); + + for mut def in macros { + let name = token::get_ident(def.ident); + seen.insert(name.clone()); + + def.use_locally = match import.as_ref() { + None => true, + Some(sel) => sel.contains_key(&name), + }; + def.export = reexport.contains_key(&name); + self.macros.push(def); + } + + if let Some(sel) = import.as_ref() { + for (name, span) in sel.iter() { + if !seen.contains(name) { + self.sess.span_err(*span, "imported macro not found"); + } + } + } + + for (name, span) in reexport.iter() { + if !seen.contains(name) { + self.sess.span_err(*span, "reexported macro not found"); + } + } + } +} diff --git a/src/librustc/metadata/mod.rs b/src/librustc/metadata/mod.rs index 24007380fee..0bf1e6d198f 100644 --- a/src/librustc/metadata/mod.rs +++ b/src/librustc/metadata/mod.rs @@ -18,3 +18,4 @@ pub mod cstore; pub mod csearch; pub mod loader; pub mod filesearch; +pub mod macro_import; diff --git a/src/librustc/plugin/load.rs b/src/librustc/plugin/load.rs index 860bfaf4ce2..1895cbcb542 100644 --- a/src/librustc/plugin/load.rs +++ b/src/librustc/plugin/load.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Used by `rustc` when loading a plugin, or a crate with exported macros. +//! Used by `rustc` when loading a plugin. use session::Session; use metadata::creader::CrateReader; @@ -17,15 +17,10 @@ use plugin::registry::Registry; use std::mem; use std::env; use std::dynamic_lib::DynamicLibrary; -use std::collections::{HashSet, HashMap}; use std::borrow::ToOwned; use syntax::ast; -use syntax::attr; use syntax::codemap::{Span, COMMAND_LINE_SP}; -use syntax::parse::token; use syntax::ptr::P; -use syntax::visit; -use syntax::visit::Visitor; use syntax::attr::AttrMetaMethods; /// Pointer to a registrar function. @@ -37,51 +32,17 @@ pub struct PluginRegistrar { pub args: Vec>, } -/// Information about loaded plugins. -pub struct Plugins { - /// Imported macros. - pub macros: Vec, - /// Registrars, as function pointers. - pub registrars: Vec, -} - -pub struct PluginLoader<'a> { +struct PluginLoader<'a> { sess: &'a Session, - span_whitelist: HashSet, reader: CrateReader<'a>, - pub plugins: Plugins, -} - -impl<'a> PluginLoader<'a> { - fn new(sess: &'a Session) -> PluginLoader<'a> { - PluginLoader { - sess: sess, - reader: CrateReader::new(sess), - span_whitelist: HashSet::new(), - plugins: Plugins { - macros: vec!(), - registrars: vec!(), - }, - } - } + plugins: Vec, } /// Read plugin metadata and dynamically load registrar functions. pub fn load_plugins(sess: &Session, krate: &ast::Crate, - addl_plugins: Option>) -> Plugins { + addl_plugins: Option>) -> Vec { let mut loader = PluginLoader::new(sess); - // We need to error on `#[macro_use] extern crate` when it isn't at the - // crate root, because `$crate` won't work properly. Identify these by - // spans, because the crate map isn't set up yet. - for item in &krate.module.items { - if let ast::ItemExternCrate(_) = item.node { - loader.span_whitelist.insert(item.span); - } - } - - visit::walk_crate(&mut loader, krate); - for attr in &krate.attrs { if !attr.check_name("plugin") { continue; @@ -112,140 +73,24 @@ pub fn load_plugins(sess: &Session, krate: &ast::Crate, } } - return loader.plugins; -} - -pub type MacroSelection = HashMap; - -// note that macros aren't expanded yet, and therefore macros can't add plugins. -impl<'a, 'v> Visitor<'v> for PluginLoader<'a> { - fn visit_item(&mut self, item: &ast::Item) { - // We're only interested in `extern crate`. - match item.node { - ast::ItemExternCrate(_) => {} - _ => { - visit::walk_item(self, item); - return; - } - } - - // Parse the attributes relating to macro loading. - let mut import = Some(HashMap::new()); // None => load all - let mut reexport = HashMap::new(); - for attr in &item.attrs { - let mut used = true; - match &attr.name()[] { - "phase" => { - self.sess.span_err(attr.span, "#[phase] is deprecated"); - } - "plugin" => { - self.sess.span_err(attr.span, "#[plugin] on `extern crate` is deprecated"); - self.sess.span_help(attr.span, &format!("use a crate attribute instead, \ - i.e. #![plugin({})]", - item.ident.as_str())[]); - } - "macro_use" => { - let names = attr.meta_item_list(); - if names.is_none() { - // no names => load all - import = None; - } - if let (Some(sel), Some(names)) = (import.as_mut(), names) { - for attr in names { - if let ast::MetaWord(ref name) = attr.node { - sel.insert(name.clone(), attr.span); - } else { - self.sess.span_err(attr.span, "bad macro import"); - } - } - } - } - "macro_reexport" => { - let names = match attr.meta_item_list() { - Some(names) => names, - None => { - self.sess.span_err(attr.span, "bad macro reexport"); - continue; - } - }; - - for attr in names { - if let ast::MetaWord(ref name) = attr.node { - reexport.insert(name.clone(), attr.span); - } else { - self.sess.span_err(attr.span, "bad macro reexport"); - } - } - } - _ => used = false, - } - if used { - attr::mark_used(attr); - } - } - - self.load_macros(item, import, reexport) - } - - fn visit_mac(&mut self, _: &ast::Mac) { - // bummer... can't see plugins inside macros. - // do nothing. - } + loader.plugins } impl<'a> PluginLoader<'a> { - pub fn load_macros<'b>(&mut self, - vi: &ast::Item, - import: Option, - reexport: MacroSelection) { - if let Some(sel) = import.as_ref() { - if sel.is_empty() && reexport.is_empty() { - return; - } - } - - 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; - } - - let macros = self.reader.read_exported_macros(vi); - let mut seen = HashSet::new(); - - for mut def in macros { - let name = token::get_ident(def.ident); - seen.insert(name.clone()); - - def.use_locally = match import.as_ref() { - None => true, - Some(sel) => sel.contains_key(&name), - }; - def.export = reexport.contains_key(&name); - self.plugins.macros.push(def); - } - - if let Some(sel) = import.as_ref() { - for (name, span) in sel.iter() { - if !seen.contains(name) { - self.sess.span_err(*span, "imported macro not found"); - } - } - } - - for (name, span) in reexport.iter() { - if !seen.contains(name) { - self.sess.span_err(*span, "reexported macro not found"); - } + fn new(sess: &'a Session) -> PluginLoader<'a> { + PluginLoader { + sess: sess, + reader: CrateReader::new(sess), + plugins: vec![], } } - pub fn load_plugin(&mut self, span: Span, name: &str, args: Vec>) { + fn load_plugin(&mut self, span: Span, name: &str, args: Vec>) { 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.registrars.push(PluginRegistrar { + self.plugins.push(PluginRegistrar { fun: fun, args: args, }); diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 12f5041cad1..5461279f17b 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -12,11 +12,11 @@ use rustc::session::Session; use rustc::session::config::{self, Input, OutputFilenames}; use rustc::session::search_paths::PathKind; use rustc::lint; +use rustc::metadata; use rustc::metadata::creader::CrateReader; use rustc::middle::{stability, ty, reachable}; use rustc::middle::dependency_format; use rustc::middle; -use rustc::plugin::load::Plugins; use rustc::plugin::registry::Registry; use rustc::plugin; use rustc::util::common::time; @@ -409,10 +409,12 @@ pub fn phase_2_configure_and_expand(sess: &Session, syntax::std_inject::maybe_inject_crates_ref(krate, sess.opts.alt_std_name.clone())); + let macros = time(time_passes, "macro loading", (), |_| + metadata::macro_import::read_macro_defs(sess, &krate)); + let mut addl_plugins = Some(addl_plugins); - let Plugins { macros, registrars } - = time(time_passes, "plugin loading", (), |_| - plugin::load::load_plugins(sess, &krate, addl_plugins.take().unwrap())); + let registrars = time(time_passes, "plugin loading", (), |_| + plugin::load::load_plugins(sess, &krate, addl_plugins.take().unwrap())); let mut registry = Registry::new(sess, &krate);