diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 80340f9a9255b..562156e70bd99 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -716,8 +716,6 @@ impl<'a> LoweringContext<'a> { id: m.id, span: m.span, imported_from: m.imported_from.map(|x| x.name), - export: m.export, - use_locally: m.use_locally, allow_internal_unstable: m.allow_internal_unstable, body: m.body.clone().into(), } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 6e81c3e700ed2..1de4355ccdfd4 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -458,8 +458,6 @@ pub struct MacroDef { pub id: NodeId, pub span: Span, pub imported_from: Option, - pub export: bool, - pub use_locally: bool, pub allow_internal_unstable: bool, pub body: HirVec, } diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index b54862ae0ade6..a3a84f51780fb 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -37,7 +37,7 @@ use util::nodemap::{NodeSet, DefIdMap}; use std::path::PathBuf; use syntax::ast; use syntax::attr; -use syntax::ext::base::MultiItemModifier; +use syntax::ext::base::SyntaxExtension; use syntax::ptr::P; use syntax::parse::token::InternedString; use syntax_pos::Span; @@ -417,18 +417,22 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn metadata_encoding_version(&self) -> &[u8] { bug!("metadata_encoding_version") } } -pub struct LoadedMacro { - pub import_site: Span, - pub kind: LoadedMacroKind, +pub enum LoadedMacros { + MacroRules(Vec), + ProcMacros(Vec<(ast::Name, SyntaxExtension)>), } -pub enum LoadedMacroKind { - Def(ast::MacroDef), - CustomDerive(String, Box), +impl LoadedMacros { + pub fn is_proc_macros(&self) -> bool { + match *self { + LoadedMacros::ProcMacros(_) => true, + _ => false, + } + } } pub trait CrateLoader { - fn load_macros(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; - fn process_item(&mut self, item: &ast::Item, defs: &Definitions); + fn process_item(&mut self, item: &ast::Item, defs: &Definitions, load_macros: bool) + -> Option; fn postprocess(&mut self, krate: &ast::Crate); } diff --git a/src/librustc_incremental/calculate_svh/svh_visitor.rs b/src/librustc_incremental/calculate_svh/svh_visitor.rs index 584e5598b9f9f..51c894e1b78f0 100644 --- a/src/librustc_incremental/calculate_svh/svh_visitor.rs +++ b/src/librustc_incremental/calculate_svh/svh_visitor.rs @@ -675,13 +675,11 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has fn visit_macro_def(&mut self, macro_def: &'tcx MacroDef) { debug!("visit_macro_def: st={:?}", self.st); - if macro_def.export { - SawMacroDef.hash(self.st); - hash_attrs!(self, ¯o_def.attrs); - visit::walk_macro_def(self, macro_def) - // FIXME(mw): We should hash the body of the macro too but we don't - // have a stable way of doing so yet. - } + SawMacroDef.hash(self.st); + hash_attrs!(self, ¯o_def.attrs); + visit::walk_macro_def(self, macro_def) + // FIXME(mw): We should hash the body of the macro too but we don't + // have a stable way of doing so yet. } } diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 7ae3f6f81079e..d160d29af7da3 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -12,19 +12,18 @@ use cstore::{self, CStore, CrateSource, MetadataBlob}; use locator::{self, CratePaths}; -use macro_import; use schema::CrateRoot; use rustc::hir::def_id::{CrateNum, DefIndex}; use rustc::hir::svh::Svh; -use rustc::middle::cstore::LoadedMacro; +use rustc::middle::cstore::LoadedMacros; use rustc::session::{config, Session}; use rustc_back::PanicStrategy; use rustc::session::search_paths::PathKind; use rustc::middle; use rustc::middle::cstore::{CrateStore, validate_crate_name, ExternCrate}; use rustc::util::nodemap::{FnvHashMap, FnvHashSet}; -use rustc::hir::map as hir_map; +use rustc::hir::map::Definitions; use std::cell::{RefCell, Cell}; use std::ops::Deref; @@ -36,7 +35,8 @@ use syntax::ast; use syntax::abi::Abi; use syntax::parse; use syntax::attr; -use syntax::parse::token::InternedString; +use syntax::ext::base::SyntaxExtension; +use syntax::parse::token::{InternedString, intern}; use syntax_pos::{self, Span, mk_sp}; use log; @@ -120,11 +120,6 @@ struct ExtensionCrate { metadata: PMDSource, dylib: Option, target_only: bool, - - ident: String, - name: String, - span: Span, - should_link: bool, } enum PMDSource { @@ -148,17 +143,6 @@ enum LoadResult { Loaded(Library), } -pub struct Macros { - pub macro_rules: Vec, - - /// An array of pairs where the first element is the name of the custom - /// derive (e.g. the trait being derived) and the second element is the - /// index of the definition. - pub custom_derive_registrar: Option, - pub svh: Svh, - pub dylib: Option, -} - impl<'a> CrateLoader<'a> { pub fn new(sess: &'a Session, cstore: &'a CStore, @@ -490,7 +474,6 @@ impl<'a> CrateLoader<'a> { info.id, info.name, info.ident, info.should_link); let target_triple = &self.sess.opts.target_triple[..]; let is_cross = target_triple != config::host_triple(); - let mut should_link = info.should_link && !is_cross; let mut target_only = false; let ident = info.ident.clone(); let name = info.name.clone(); @@ -517,7 +500,6 @@ impl<'a> CrateLoader<'a> { // Try loading from target crates. This will abort later if we // try to load a plugin registrar function, target_only = true; - should_link = info.should_link; locate_ctxt.target = &self.sess.target.target; locate_ctxt.triple = target_triple; @@ -547,25 +529,14 @@ impl<'a> CrateLoader<'a> { metadata: metadata, dylib: dylib.map(|p| p.0), target_only: target_only, - name: info.name.to_string(), - ident: info.ident.to_string(), - span: span, - should_link: should_link, } } - pub fn read_macros(&mut self, item: &ast::Item) -> Macros { - let ci = self.extract_crate_info(item).unwrap(); - let ekrate = self.read_extension_crate(item.span, &ci); - + fn read_macros(&mut self, item: &ast::Item, ekrate: &ExtensionCrate) -> LoadedMacros { let root = ekrate.metadata.get_root(); let source_name = format!("<{} macros>", item.ident); - let mut ret = Macros { - macro_rules: Vec::new(), - custom_derive_registrar: None, - svh: root.hash, - dylib: None, - }; + let mut macro_rules = Vec::new(); + for def in root.macro_defs.decode(&*ekrate.metadata) { // NB: Don't use parse::parse_tts_from_source_str because it parses with // quote_depth > 0. @@ -589,54 +560,90 @@ impl<'a> CrateLoader<'a> { attr::mark_used(attr); } - ret.macro_rules.push(ast::MacroDef { + macro_rules.push(ast::MacroDef { ident: ast::Ident::with_empty_ctxt(def.name), - attrs: def.attrs, id: ast::DUMMY_NODE_ID, span: local_span, imported_from: Some(item.ident), - // overridden in plugin/load.rs - export: false, - use_locally: false, - allow_internal_unstable: false, - + allow_internal_unstable: attr::contains_name(&def.attrs, "allow_internal_unstable"), + attrs: def.attrs, body: body, }); self.sess.imported_macro_spans.borrow_mut() .insert(local_span, (def.name.as_str().to_string(), def.span)); } - match root.macro_derive_registrar { - Some(id) => ret.custom_derive_registrar = Some(id), - - // If this crate is not a proc-macro crate then we might be able to - // register it with the local crate store to prevent loading the - // metadata twice. - // - // If it's a proc-macro crate, though, then we definitely don't - // want to register it with the local crate store as we're just - // going to use it as we would a plugin. - None => { - ekrate.register(self); - return ret + if let Some(id) = root.macro_derive_registrar { + let dylib = match ekrate.dylib.clone() { + Some(dylib) => dylib, + None => span_bug!(item.span, "proc-macro crate not dylib"), + }; + if ekrate.target_only { + let message = format!("proc-macro crate is not available for \ + triple `{}` (only found {})", + config::host_triple(), + self.sess.opts.target_triple); + self.sess.span_fatal(item.span, &message); } - } - self.cstore.add_used_for_derive_macros(item); - ret.dylib = ekrate.dylib.clone(); - if ret.dylib.is_none() { - span_bug!(item.span, "proc-macro crate not dylib"); + // custom derive crates currently should not have any macro_rules! + // exported macros, enforced elsewhere + assert_eq!(macro_rules.len(), 0); + LoadedMacros::ProcMacros(self.load_derive_macros(item, id, root.hash, dylib)) + } else { + LoadedMacros::MacroRules(macro_rules) } + } - if ekrate.target_only { - let message = format!("proc-macro crate is not available for \ - triple `{}` (only found {})", - config::host_triple(), - self.sess.opts.target_triple); - self.sess.span_fatal(item.span, &message); + /// Load custom derive macros. + /// + /// Note that this is intentionally similar to how we load plugins today, + /// but also intentionally separate. Plugins are likely always going to be + /// implemented as dynamic libraries, but we have a possible future where + /// custom derive (and other macro-1.1 style features) are implemented via + /// executables and custom IPC. + fn load_derive_macros(&mut self, item: &ast::Item, index: DefIndex, svh: Svh, path: PathBuf) + -> Vec<(ast::Name, SyntaxExtension)> { + use std::{env, mem}; + use proc_macro::TokenStream; + use proc_macro::__internal::Registry; + use rustc_back::dynamic_lib::DynamicLibrary; + use syntax_ext::deriving::custom::CustomDerive; + + // 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, + Err(err) => self.sess.span_fatal(item.span, &err), + }; + + let sym = self.sess.generate_derive_registrar_symbol(&svh, index); + let registrar = unsafe { + let sym = match lib.symbol(&sym) { + Ok(f) => f, + Err(err) => self.sess.span_fatal(item.span, &err), + }; + mem::transmute::<*mut u8, fn(&mut Registry)>(sym) + }; + + struct MyRegistrar(Vec<(ast::Name, SyntaxExtension)>); + + impl Registry for MyRegistrar { + fn register_custom_derive(&mut self, + trait_name: &str, + expand: fn(TokenStream) -> TokenStream) { + let derive = SyntaxExtension::CustomDerive(Box::new(CustomDerive::new(expand))); + self.0.push((intern(trait_name), derive)); + } } - return ret + let mut my_registrar = MyRegistrar(Vec::new()); + registrar(&mut my_registrar); + + // Intentionally leak the dynamic library. We can't ever unload it + // since the library can make things that will live arbitrarily long. + mem::forget(lib); + my_registrar.0 } /// Look for a plugin registrar. Returns library path, crate @@ -889,22 +896,6 @@ impl<'a> CrateLoader<'a> { } } -impl ExtensionCrate { - fn register(self, loader: &mut CrateLoader) { - if !self.should_link { - return - } - - let library = match self.metadata { - PMDSource::Owned(lib) => lib, - PMDSource::Registered(_) => return, - }; - - // Register crate now to avoid double-reading metadata - loader.register_crate(&None, &self.ident, &self.name, self.span, library, true); - } -} - impl<'a> CrateLoader<'a> { pub fn preprocess(&mut self, krate: &ast::Crate) { for attr in krate.attrs.iter().filter(|m| m.name() == "link_args") { @@ -990,47 +981,56 @@ impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> { self.register_statically_included_foreign_items(); } - fn process_item(&mut self, item: &ast::Item, definitions: &hir_map::Definitions) { + fn process_item(&mut self, item: &ast::Item, definitions: &Definitions, load_macros: bool) + -> Option { match item.node { ast::ItemKind::ExternCrate(_) => {} - ast::ItemKind::ForeignMod(ref fm) => return self.process_foreign_mod(item, fm), - _ => return, + ast::ItemKind::ForeignMod(ref fm) => { + self.process_foreign_mod(item, fm); + return None; + } + _ => return None, } - // If this `extern crate` item has `#[macro_use]` then we can safely skip it. - // These annotations were processed during macro expansion and are already loaded - // (if necessary) into our crate store. - // - // Note that it's important we *don't* fall through below as some `#[macro_use]` - // crates are explicitly not linked (e.g. macro crates) so we want to ensure - // we avoid `resolve_crate` with those. - if attr::contains_name(&item.attrs, "macro_use") { - if self.cstore.was_used_for_derive_macros(item) { - return + let info = self.extract_crate_info(item).unwrap(); + let loaded_macros = if load_macros { + let ekrate = self.read_extension_crate(item.span, &info); + let loaded_macros = self.read_macros(item, &ekrate); + + // If this is a proc-macro crate or `#[no_link]` crate, it is only used at compile time, + // so we return here to avoid registering the crate. + if loaded_macros.is_proc_macros() || !info.should_link { + return Some(loaded_macros); } - } - if let Some(info) = self.extract_crate_info(item) { - if !info.should_link { - return; + // Register crate now to avoid double-reading metadata + if let PMDSource::Owned(lib) = ekrate.metadata { + if ekrate.target_only || config::host_triple() == self.sess.opts.target_triple { + let ExternCrateInfo { ref ident, ref name, .. } = info; + self.register_crate(&None, ident, name, item.span, lib, true); + } } - let (cnum, ..) = self.resolve_crate( - &None, &info.ident, &info.name, None, item.span, PathKind::Crate, true, - ); + Some(loaded_macros) + } else { + if !info.should_link { + return None; + } + None + }; - let def_id = definitions.opt_local_def_id(item.id).unwrap(); - let len = definitions.def_path(def_id.index).data.len(); + let (cnum, ..) = self.resolve_crate( + &None, &info.ident, &info.name, None, item.span, PathKind::Crate, true, + ); - let extern_crate = - ExternCrate { def_id: def_id, span: item.span, direct: true, path_len: len }; - self.update_extern_crate(cnum, extern_crate, &mut FnvHashSet()); + let def_id = definitions.opt_local_def_id(item.id).unwrap(); + let len = definitions.def_path(def_id.index).data.len(); - self.cstore.add_extern_mod_stmt_cnum(info.id, cnum); - } - } + let extern_crate = + ExternCrate { def_id: def_id, span: item.span, direct: true, path_len: len }; + self.update_extern_crate(cnum, extern_crate, &mut FnvHashSet()); + self.cstore.add_extern_mod_stmt_cnum(info.id, cnum); - fn load_macros(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { - macro_import::load_macros(self, extern_crate, allows_macros) + loaded_macros } } diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index a87e61c4c944c..781ebf81d59df 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -21,14 +21,13 @@ use rustc::hir::svh::Svh; use rustc::middle::cstore::ExternCrate; use rustc_back::PanicStrategy; use rustc_data_structures::indexed_vec::IndexVec; -use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap, FnvHashSet}; +use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap}; use std::cell::{RefCell, Cell}; use std::rc::Rc; use std::path::PathBuf; use flate::Bytes; -use syntax::ast::{self, Ident}; -use syntax::attr; +use syntax::{ast, attr}; use syntax_pos; pub use rustc::middle::cstore::{NativeLibraryKind, LinkagePreference}; @@ -105,7 +104,6 @@ pub struct CStore { pub inlined_item_cache: RefCell>>, pub defid_for_inlined_node: RefCell>, pub visible_parent_map: RefCell>, - pub used_for_derive_macro: RefCell>, } impl CStore { @@ -121,7 +119,6 @@ impl CStore { visible_parent_map: RefCell::new(FnvHashMap()), inlined_item_cache: RefCell::new(FnvHashMap()), defid_for_inlined_node: RefCell::new(FnvHashMap()), - used_for_derive_macro: RefCell::new(FnvHashSet()), } } @@ -277,14 +274,6 @@ impl CStore { { self.extern_mod_crate_map.borrow().get(&emod_id).cloned() } - - pub fn was_used_for_derive_macros(&self, i: &ast::Item) -> bool { - self.used_for_derive_macro.borrow().contains(&i.ident) - } - - pub fn add_used_for_derive_macros(&self, i: &ast::Item) { - self.used_for_derive_macro.borrow_mut().insert(i.ident); - } } impl CrateMetadata { diff --git a/src/librustc_metadata/diagnostics.rs b/src/librustc_metadata/diagnostics.rs index c03375bf8254f..b2f4760727ae1 100644 --- a/src/librustc_metadata/diagnostics.rs +++ b/src/librustc_metadata/diagnostics.rs @@ -91,185 +91,6 @@ You need to link your code to the relevant crate in order to be able to use it well, and you link to them the same way. "##, -E0466: r##" -Macro import declarations were malformed. - -Erroneous code examples: - -```compile_fail,E0466 -#[macro_use(a_macro(another_macro))] // error: invalid import declaration -extern crate core as some_crate; - -#[macro_use(i_want = "some_macros")] // error: invalid import declaration -extern crate core as another_crate; -``` - -This is a syntax error at the level of attribute declarations. The proper -syntax for macro imports is the following: - -```ignore -// In some_crate: -#[macro_export] -macro_rules! get_tacos { - ... -} - -#[macro_export] -macro_rules! get_pimientos { - ... -} - -// In your crate: -#[macro_use(get_tacos, get_pimientos)] // It imports `get_tacos` and -extern crate some_crate; // `get_pimientos` macros from some_crate -``` - -If you would like to import all exported macros, write `macro_use` with no -arguments. -"##, - -E0467: r##" -Macro reexport declarations were empty or malformed. - -Erroneous code examples: - -```compile_fail,E0467 -#[macro_reexport] // error: no macros listed for export -extern crate core as macros_for_good; - -#[macro_reexport(fun_macro = "foo")] // error: not a macro identifier -extern crate core as other_macros_for_good; -``` - -This is a syntax error at the level of attribute declarations. - -Currently, `macro_reexport` requires at least one macro name to be listed. -Unlike `macro_use`, listing no names does not reexport all macros from the -given crate. - -Decide which macros you would like to export and list them properly. - -These are proper reexport declarations: - -```ignore -#[macro_reexport(some_macro, another_macro)] -extern crate macros_for_good; -``` -"##, - -E0468: r##" -A non-root module attempts to import macros from another crate. - -Example of erroneous code: - -```compile_fail,E0468 -mod foo { - #[macro_use(helpful_macro)] // error: must be at crate root to import - extern crate core; // macros from another crate - helpful_macro!(...); -} -``` - -Only `extern crate` imports at the crate root level are allowed to import -macros. - -Either move the macro import to crate root or do without the foreign macros. -This will work: - -```ignore -#[macro_use(helpful_macro)] -extern crate some_crate; - -mod foo { - helpful_macro!(...) -} -``` -"##, - -E0469: r##" -A macro listed for import was not found. - -Erroneous code example: - -```compile_fail,E0469 -#[macro_use(drink, be_merry)] // error: imported macro not found -extern crate collections; - -fn main() { - // ... -} -``` - -Either the listed macro is not contained in the imported crate, or it is not -exported from the given crate. - -This could be caused by a typo. Did you misspell the macro's name? - -Double-check the names of the macros listed for import, and that the crate -in question exports them. - -A working version would be: - -```ignore -// In some_crate crate: -#[macro_export] -macro_rules! eat { - ... -} - -#[macro_export] -macro_rules! drink { - ... -} - -// In your crate: -#[macro_use(eat, drink)] -extern crate some_crate; //ok! -``` -"##, - -E0470: r##" -A macro listed for reexport was not found. - -Erroneous code example: - -```compile_fail,E0470 -#[macro_reexport(drink, be_merry)] -extern crate collections; - -fn main() { - // ... -} -``` - -Either the listed macro is not contained in the imported crate, or it is not -exported from the given crate. - -This could be caused by a typo. Did you misspell the macro's name? - -Double-check the names of the macros listed for reexport, and that the crate -in question exports them. - -A working version: - -```ignore -// In some_crate crate: -#[macro_export] -macro_rules! eat { - ... -} - -#[macro_export] -macro_rules! drink { - ... -} - -// In your_crate: -#[macro_reexport(eat, drink)] -extern crate some_crate; -``` -"##, - } register_diagnostics! { diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index c0fc1a7065cc0..300c5f0dec71d 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -59,6 +59,5 @@ mod schema; pub mod creader; pub mod cstore; pub mod locator; -pub mod macro_import; __build_diagnostic_array! { librustc_metadata, DIAGNOSTICS } diff --git a/src/librustc_metadata/macro_import.rs b/src/librustc_metadata/macro_import.rs deleted file mode 100644 index ddc254a16d9f4..0000000000000 --- a/src/librustc_metadata/macro_import.rs +++ /dev/null @@ -1,235 +0,0 @@ -// 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 std::collections::HashSet; -use std::env; -use std::mem; - -use creader::{CrateLoader, Macros}; - -use proc_macro::TokenStream; -use proc_macro::__internal::Registry; -use rustc::hir::def_id::DefIndex; -use rustc::middle::cstore::{LoadedMacro, LoadedMacroKind}; -use rustc::session::Session; -use rustc::util::nodemap::FnvHashMap; -use rustc_back::dynamic_lib::DynamicLibrary; -use syntax::ast; -use syntax::attr; -use syntax::parse::token; -use syntax_ext::deriving::custom::CustomDerive; -use syntax_pos::{Span, DUMMY_SP}; - -pub fn call_bad_macro_reexport(a: &Session, b: Span) { - span_err!(a, b, E0467, "bad macro reexport"); -} - -pub type MacroSelection = FnvHashMap; - -enum ImportSelection { - All(Span), - Some(MacroSelection), -} - -pub fn load_macros(loader: &mut CrateLoader, extern_crate: &ast::Item, allows_macros: bool) - -> Vec { - loader.load_crate(extern_crate, allows_macros) -} - -impl<'a> CrateLoader<'a> { - fn load_crate(&mut self, - extern_crate: &ast::Item, - allows_macros: bool) -> Vec { - // Parse the attributes relating to macros. - let mut import = ImportSelection::Some(FnvHashMap()); - let mut reexport = FnvHashMap(); - let mut no_link = false; - - for attr in &extern_crate.attrs { - let mut used = true; - match &attr.name()[..] { - "macro_use" => { - let names = attr.meta_item_list(); - if names.is_none() { - import = ImportSelection::All(attr.span); - } else if let ImportSelection::Some(ref mut sel) = import { - for attr in names.unwrap() { - if let Some(word) = attr.word() { - sel.insert(word.name().clone(), attr.span()); - } else { - span_err!(self.sess, attr.span(), E0466, "bad macro import"); - } - } - } - } - "macro_reexport" => { - let names = match attr.meta_item_list() { - Some(names) => names, - None => { - call_bad_macro_reexport(self.sess, attr.span); - continue; - } - }; - - for attr in names { - if let Some(word) = attr.word() { - reexport.insert(word.name().clone(), attr.span()); - } else { - call_bad_macro_reexport(self.sess, attr.span()); - } - } - } - "no_link" => no_link = true, - _ => used = false, - } - if used { - attr::mark_used(attr); - } - } - - self.load_macros(extern_crate, allows_macros, import, reexport, no_link) - } - - fn load_macros<'b>(&mut self, - vi: &ast::Item, - allows_macros: bool, - import: ImportSelection, - reexport: MacroSelection, - no_link: bool) - -> Vec { - if let ImportSelection::Some(ref sel) = import { - if sel.is_empty() && reexport.is_empty() { - // Make sure we can read macros from `#[no_link]` crates. - if no_link { - self.read_macros(vi); - } - return Vec::new(); - } - } - - if !allows_macros { - span_err!(self.sess, vi.span, E0468, - "an `extern crate` loading macros must be at the crate root"); - return Vec::new(); - } - - let mut macros = self.read_macros(vi); - let mut ret = Vec::new(); - let mut seen = HashSet::new(); - - for mut def in macros.macro_rules.drain(..) { - let name = def.ident.name.as_str(); - - let import_site = match import { - ImportSelection::All(span) => Some(span), - ImportSelection::Some(ref sel) => sel.get(&name).cloned() - }; - def.use_locally = import_site.is_some(); - def.export = reexport.contains_key(&name); - def.allow_internal_unstable = attr::contains_name(&def.attrs, - "allow_internal_unstable"); - debug!("load_macros: loaded: {:?}", def); - ret.push(LoadedMacro { - kind: LoadedMacroKind::Def(def), - import_site: import_site.unwrap_or(DUMMY_SP), - }); - seen.insert(name); - } - - if let Some(index) = macros.custom_derive_registrar { - // custom derive crates currently should not have any macro_rules! - // exported macros, enforced elsewhere - assert_eq!(ret.len(), 0); - - if let ImportSelection::Some(..) = import { - self.sess.span_err(vi.span, "`proc-macro` crates cannot be \ - selectively imported from, must \ - use `#[macro_use]`"); - } - - if reexport.len() > 0 { - self.sess.span_err(vi.span, "`proc-macro` crates cannot be \ - reexported from"); - } - - self.load_derive_macros(vi.span, ¯os, index, &mut ret); - } - - if let ImportSelection::Some(sel) = import { - for (name, span) in sel { - if !seen.contains(&name) { - span_err!(self.sess, span, E0469, - "imported macro not found"); - } - } - } - - for (name, span) in &reexport { - if !seen.contains(&name) { - span_err!(self.sess, *span, E0470, - "reexported macro not found"); - } - } - - return ret - } - - /// Load the custom derive macros into the list of macros we're loading. - /// - /// Note that this is intentionally similar to how we load plugins today, - /// but also intentionally separate. Plugins are likely always going to be - /// implemented as dynamic libraries, but we have a possible future where - /// custom derive (and other macro-1.1 style features) are implemented via - /// executables and custom IPC. - fn load_derive_macros(&mut self, - span: Span, - macros: &Macros, - index: DefIndex, - ret: &mut Vec) { - // Make sure the path contains a / or the linker will search for it. - let path = macros.dylib.as_ref().unwrap(); - let path = env::current_dir().unwrap().join(path); - let lib = match DynamicLibrary::open(Some(&path)) { - Ok(lib) => lib, - Err(err) => self.sess.span_fatal(span, &err), - }; - - let sym = self.sess.generate_derive_registrar_symbol(¯os.svh, index); - let registrar = unsafe { - let sym = match lib.symbol(&sym) { - Ok(f) => f, - Err(err) => self.sess.span_fatal(span, &err), - }; - mem::transmute::<*mut u8, fn(&mut Registry)>(sym) - }; - - struct MyRegistrar<'a>(&'a mut Vec, Span); - - impl<'a> Registry for MyRegistrar<'a> { - fn register_custom_derive(&mut self, - trait_name: &str, - expand: fn(TokenStream) -> TokenStream) { - let derive = Box::new(CustomDerive::new(expand)); - self.0.push(LoadedMacro { - kind: LoadedMacroKind::CustomDerive(trait_name.to_string(), derive), - import_site: self.1, - }); - } - } - - registrar(&mut MyRegistrar(ret, span)); - - // Intentionally leak the dynamic library. We can't ever unload it - // since the library can make things that will live arbitrarily long. - mem::forget(lib); - } -} diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 9ed7be5af4e55..db86840fd381c 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -21,10 +21,11 @@ use {NameBinding, NameBindingKind, ToNameBinding}; use Resolver; use {resolve_error, resolve_struct_error, ResolutionError}; -use rustc::middle::cstore::LoadedMacroKind; +use rustc::middle::cstore::LoadedMacros; use rustc::hir::def::*; use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; use rustc::ty; +use rustc::util::nodemap::FnvHashMap; use std::cell::Cell; use std::rc::Rc; @@ -58,6 +59,14 @@ impl<'a> ToNameBinding<'a> for (Def, Span, ty::Visibility) { } } +#[derive(Default, PartialEq, Eq)] +struct LegacyMacroImports { + import_all: Option, + imports: Vec<(Name, Span)>, + reexports: Vec<(Name, Span)>, + no_link: bool, +} + impl<'b> Resolver<'b> { /// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined; /// otherwise, reports an error. @@ -193,57 +202,27 @@ impl<'b> Resolver<'b> { } ItemKind::ExternCrate(_) => { - // We need to error on `#[macro_use] extern crate` when it isn't at the - // crate root, because `$crate` won't work properly. - let is_crate_root = self.current_module.parent.is_none(); - let import_macro = |this: &mut Self, name, ext, span| { - let shadowing = this.builtin_macros.insert(name, Rc::new(ext)).is_some(); - if shadowing && expansion != Mark::root() { - let msg = format!("`{}` is already in scope", name); - this.session.struct_span_err(span, &msg) - .note("macro-expanded `#[macro_use]`s may not shadow \ - existing macros (see RFC 1560)") - .emit(); - } - }; - - let mut custom_derive_crate = false; - // The mark of the expansion that generates the loaded macros. - let mut opt_mark = None; - for loaded_macro in self.crate_loader.load_macros(item, is_crate_root) { - let mark = opt_mark.unwrap_or_else(Mark::fresh); - opt_mark = Some(mark); - match loaded_macro.kind { - LoadedMacroKind::Def(mut def) => { - if def.use_locally { - self.macro_names.insert(def.ident.name); - def.body = mark_tts(&def.body, mark); - let ext = macro_rules::compile(&self.session.parse_sess, &def); - import_macro(self, def.ident.name, ext, loaded_macro.import_site); - } - if def.export { - def.id = self.next_node_id(); - self.exported_macros.push(def); - } - } - LoadedMacroKind::CustomDerive(name, ext) => { - custom_derive_crate = true; - let ext = SyntaxExtension::CustomDerive(ext); - import_macro(self, token::intern(&name), ext, loaded_macro.import_site); - } + let legacy_imports = self.legacy_macro_imports(&item.attrs); + // `#[macro_use]` and `#[macro_reexport]` are only allowed at the crate root. + if self.current_module.parent.is_some() && { + legacy_imports.import_all.is_some() || !legacy_imports.imports.is_empty() || + !legacy_imports.reexports.is_empty() + } { + if self.current_module.parent.is_some() { + span_err!(self.session, item.span, E0468, + "an `extern crate` loading macros must be at the crate root"); } } - if custom_derive_crate && !self.session.features.borrow().proc_macro { - let issue = feature_gate::GateIssue::Language; - let msg = "loading custom derive macro crates is experimentally supported"; - emit_feature_err(&self.session.parse_sess, "proc_macro", item.span, issue, msg); - } - - self.crate_loader.process_item(item, &self.definitions); + let loaded_macros = if legacy_imports != LegacyMacroImports::default() { + self.crate_loader.process_item(item, &self.definitions, true) + } else { + self.crate_loader.process_item(item, &self.definitions, false) + }; // n.b. we don't need to look at the path option here, because cstore already did - if let Some(crate_id) = self.session.cstore.extern_mod_stmt_cnum(item.id) { + let crate_id = self.session.cstore.extern_mod_stmt_cnum(item.id); + let module = if let Some(crate_id) = crate_id { let def_id = DefId { krate: crate_id, index: CRATE_DEF_INDEX, @@ -254,25 +233,21 @@ impl<'b> Resolver<'b> { ..ModuleS::new(Some(parent), ModuleKind::Def(Def::Mod(def_id), name)) }); self.define(parent, name, TypeNS, (module, sp, vis)); - - if let Some(mark) = opt_mark { - let invocation = self.arenas.alloc_invocation_data(InvocationData { - module: Cell::new(module), - def_index: CRATE_DEF_INDEX, - const_integer: false, - legacy_scope: Cell::new(LegacyScope::Empty), - expansion: Cell::new(LegacyScope::Empty), - }); - self.invocations.insert(mark, invocation); - } - self.populate_module_if_necessary(module); + module } else { // Define an empty module let def = Def::Mod(self.definitions.local_def_id(item.id)); let module = ModuleS::new(Some(parent), ModuleKind::Def(def, name)); let module = self.arenas.alloc_module(module); self.define(parent, name, TypeNS, (module, sp, vis)); + module + }; + + if let Some(loaded_macros) = loaded_macros { + self.import_extern_crate_macros( + item, module, loaded_macros, legacy_imports, expansion == Mark::root(), + ); } } @@ -294,7 +269,9 @@ impl<'b> Resolver<'b> { self.current_module = module; } - ItemKind::ForeignMod(..) => self.crate_loader.process_item(item, &self.definitions), + ItemKind::ForeignMod(..) => { + self.crate_loader.process_item(item, &self.definitions, false); + } // These items live in the value namespace. ItemKind::Static(_, m, _) => { @@ -516,6 +493,93 @@ impl<'b> Resolver<'b> { module.populated.set(true) } + fn import_extern_crate_macros(&mut self, + extern_crate: &Item, + module: Module<'b>, + loaded_macros: LoadedMacros, + legacy_imports: LegacyMacroImports, + allow_shadowing: bool) { + let import_macro = |this: &mut Self, name, ext: Rc<_>, span| { + if let SyntaxExtension::NormalTT(..) = *ext { + this.macro_names.insert(name); + } + if this.builtin_macros.insert(name, ext).is_some() && !allow_shadowing { + let msg = format!("`{}` is already in scope", name); + let note = + "macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)"; + this.session.struct_span_err(span, &msg).note(note).emit(); + } + }; + + match loaded_macros { + LoadedMacros::MacroRules(macros) => { + let mark = Mark::fresh(); + if !macros.is_empty() { + let invocation = self.arenas.alloc_invocation_data(InvocationData { + module: Cell::new(module), + def_index: CRATE_DEF_INDEX, + const_integer: false, + legacy_scope: Cell::new(LegacyScope::Empty), + expansion: Cell::new(LegacyScope::Empty), + }); + self.invocations.insert(mark, invocation); + } + + let mut macros: FnvHashMap<_, _> = macros.into_iter().map(|mut def| { + def.body = mark_tts(&def.body, mark); + let ext = macro_rules::compile(&self.session.parse_sess, &def); + (def.ident.name, (def, Rc::new(ext))) + }).collect(); + + if let Some(span) = legacy_imports.import_all { + for (&name, &(_, ref ext)) in macros.iter() { + import_macro(self, name, ext.clone(), span); + } + } else { + for (name, span) in legacy_imports.imports { + if let Some(&(_, ref ext)) = macros.get(&name) { + import_macro(self, name, ext.clone(), span); + } else { + span_err!(self.session, span, E0469, "imported macro not found"); + } + } + } + for (name, span) in legacy_imports.reexports { + if let Some((mut def, _)) = macros.remove(&name) { + def.id = self.next_node_id(); + self.exported_macros.push(def); + } else { + span_err!(self.session, span, E0470, "reexported macro not found"); + } + } + } + + LoadedMacros::ProcMacros(macros) => { + if !self.session.features.borrow().proc_macro { + let sess = &self.session.parse_sess; + let issue = feature_gate::GateIssue::Language; + let msg = + "loading custom derive macro crates is experimentally supported"; + emit_feature_err(sess, "proc_macro", extern_crate.span, issue, msg); + } + if !legacy_imports.imports.is_empty() { + let msg = "`proc-macro` crates cannot be selectively imported from, \ + must use `#[macro_use]`"; + self.session.span_err(extern_crate.span, msg); + } + if !legacy_imports.reexports.is_empty() { + let msg = "`proc-macro` crates cannot be reexported from"; + self.session.span_err(extern_crate.span, msg); + } + if let Some(span) = legacy_imports.import_all { + for (name, ext) in macros { + import_macro(self, name, Rc::new(ext), span); + } + } + } + } + } + // does this attribute list contain "macro_use"? fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { for attr in attrs { @@ -539,6 +603,42 @@ impl<'b> Resolver<'b> { false } + + fn legacy_macro_imports(&mut self, attrs: &[ast::Attribute]) -> LegacyMacroImports { + let mut imports = LegacyMacroImports::default(); + for attr in attrs { + if attr.check_name("macro_use") { + match attr.meta_item_list() { + Some(names) => for attr in names { + if let Some(word) = attr.word() { + imports.imports.push((token::intern(&word.name()), attr.span())); + } else { + span_err!(self.session, attr.span(), E0466, "bad macro import"); + } + }, + None => imports.import_all = Some(attr.span), + } + } else if attr.check_name("macro_reexport") { + let bad_macro_reexport = |this: &mut Self, span| { + span_err!(this.session, span, E0467, "bad macro reexport"); + }; + if let Some(names) = attr.meta_item_list() { + for attr in names { + if let Some(word) = attr.word() { + imports.reexports.push((token::intern(&word.name()), attr.span())); + } else { + bad_macro_reexport(self, attr.span()); + } + } + } else { + bad_macro_reexport(self, attr.span()); + } + } else if attr.check_name("no_link") { + imports.no_link = true; + } + } + imports + } } pub struct BuildReducedGraphVisitor<'a, 'b: 'a> { diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index f8f90bdb4e7f5..f2a5aedbb3ab4 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -1272,6 +1272,185 @@ impl Foo for i32 {} ``` "##, +E0466: r##" +Macro import declarations were malformed. + +Erroneous code examples: + +```compile_fail,E0466 +#[macro_use(a_macro(another_macro))] // error: invalid import declaration +extern crate core as some_crate; + +#[macro_use(i_want = "some_macros")] // error: invalid import declaration +extern crate core as another_crate; +``` + +This is a syntax error at the level of attribute declarations. The proper +syntax for macro imports is the following: + +```ignore +// In some_crate: +#[macro_export] +macro_rules! get_tacos { + ... +} + +#[macro_export] +macro_rules! get_pimientos { + ... +} + +// In your crate: +#[macro_use(get_tacos, get_pimientos)] // It imports `get_tacos` and +extern crate some_crate; // `get_pimientos` macros from some_crate +``` + +If you would like to import all exported macros, write `macro_use` with no +arguments. +"##, + +E0467: r##" +Macro reexport declarations were empty or malformed. + +Erroneous code examples: + +```compile_fail,E0467 +#[macro_reexport] // error: no macros listed for export +extern crate core as macros_for_good; + +#[macro_reexport(fun_macro = "foo")] // error: not a macro identifier +extern crate core as other_macros_for_good; +``` + +This is a syntax error at the level of attribute declarations. + +Currently, `macro_reexport` requires at least one macro name to be listed. +Unlike `macro_use`, listing no names does not reexport all macros from the +given crate. + +Decide which macros you would like to export and list them properly. + +These are proper reexport declarations: + +```ignore +#[macro_reexport(some_macro, another_macro)] +extern crate macros_for_good; +``` +"##, + +E0468: r##" +A non-root module attempts to import macros from another crate. + +Example of erroneous code: + +```compile_fail,E0468 +mod foo { + #[macro_use(helpful_macro)] // error: must be at crate root to import + extern crate core; // macros from another crate + helpful_macro!(...); +} +``` + +Only `extern crate` imports at the crate root level are allowed to import +macros. + +Either move the macro import to crate root or do without the foreign macros. +This will work: + +```ignore +#[macro_use(helpful_macro)] +extern crate some_crate; + +mod foo { + helpful_macro!(...) +} +``` +"##, + +E0469: r##" +A macro listed for import was not found. + +Erroneous code example: + +```compile_fail,E0469 +#[macro_use(drink, be_merry)] // error: imported macro not found +extern crate collections; + +fn main() { + // ... +} +``` + +Either the listed macro is not contained in the imported crate, or it is not +exported from the given crate. + +This could be caused by a typo. Did you misspell the macro's name? + +Double-check the names of the macros listed for import, and that the crate +in question exports them. + +A working version would be: + +```ignore +// In some_crate crate: +#[macro_export] +macro_rules! eat { + ... +} + +#[macro_export] +macro_rules! drink { + ... +} + +// In your crate: +#[macro_use(eat, drink)] +extern crate some_crate; //ok! +``` +"##, + +E0470: r##" +A macro listed for reexport was not found. + +Erroneous code example: + +```compile_fail,E0470 +#[macro_reexport(drink, be_merry)] +extern crate collections; + +fn main() { + // ... +} +``` + +Either the listed macro is not contained in the imported crate, or it is not +exported from the given crate. + +This could be caused by a typo. Did you misspell the macro's name? + +Double-check the names of the macros listed for reexport, and that the crate +in question exports them. + +A working version: + +```ignore +// In some_crate crate: +#[macro_export] +macro_rules! eat { + ... +} + +#[macro_export] +macro_rules! drink { + ... +} + +// In your_crate: +#[macro_reexport(eat, drink)] +extern crate some_crate; +``` +"##, + E0530: r##" A binding shadowed something it shouldn't. diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 0f42b4520c9b7..72e5823598ea1 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -114,22 +114,22 @@ impl<'a> base::Resolver for Resolver<'a> { invocation.expansion.set(visitor.legacy_scope); } - fn add_macro(&mut self, scope: Mark, mut def: ast::MacroDef) { + fn add_macro(&mut self, scope: Mark, mut def: ast::MacroDef, export: bool) { if &def.ident.name.as_str() == "macro_rules" { self.session.span_err(def.span, "user-defined macros may not be named `macro_rules`"); } - if def.use_locally { - let invocation = self.invocations[&scope]; - let binding = self.arenas.alloc_legacy_binding(LegacyBinding { - parent: invocation.legacy_scope.get(), - name: def.ident.name, - ext: Rc::new(macro_rules::compile(&self.session.parse_sess, &def)), - span: def.span, - }); - invocation.legacy_scope.set(LegacyScope::Binding(binding)); - self.macro_names.insert(def.ident.name); - } - if def.export { + + let invocation = self.invocations[&scope]; + let binding = self.arenas.alloc_legacy_binding(LegacyBinding { + parent: invocation.legacy_scope.get(), + name: def.ident.name, + ext: Rc::new(macro_rules::compile(&self.session.parse_sess, &def)), + span: def.span, + }); + invocation.legacy_scope.set(LegacyScope::Binding(binding)); + self.macro_names.insert(def.ident.name); + + if export { def.id = self.next_node_id(); self.exported_macros.push(def); } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 30fc4c3dd8045..ae036e66c6903 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -2012,8 +2012,6 @@ pub struct MacroDef { pub id: NodeId, pub span: Span, pub imported_from: Option, - pub export: bool, - pub use_locally: bool, pub allow_internal_unstable: bool, pub body: Vec, } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index f3272960e8313..c404c6d1162f9 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -519,7 +519,7 @@ pub trait Resolver { fn get_module_scope(&mut self, id: ast::NodeId) -> Mark; fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion); - fn add_macro(&mut self, scope: Mark, def: ast::MacroDef); + fn add_macro(&mut self, scope: Mark, def: ast::MacroDef, export: bool); fn add_ext(&mut self, ident: ast::Ident, ext: Rc); fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec); @@ -541,7 +541,7 @@ impl Resolver for DummyResolver { fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() } fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {} - fn add_macro(&mut self, _scope: Mark, _def: ast::MacroDef) {} + fn add_macro(&mut self, _scope: Mark, _def: ast::MacroDef, _export: bool) {} fn add_ext(&mut self, _ident: ast::Ident, _ext: Rc) {} fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec) {} diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 61911e0d3b3d5..ceca698f479ab 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -157,14 +157,13 @@ impl IdentMacroExpander for MacroRulesExpander { tts: Vec, attrs: Vec) -> Box { + let export = attr::contains_name(&attrs, "macro_export"); let def = ast::MacroDef { ident: ident, id: ast::DUMMY_NODE_ID, span: span, imported_from: None, - use_locally: true, body: tts, - export: attr::contains_name(&attrs, "macro_export"), allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"), attrs: attrs, }; @@ -176,7 +175,7 @@ impl IdentMacroExpander for MacroRulesExpander { MacEager::items(placeholders::macro_scope_placeholder().make_items()) }; - cx.resolver.add_macro(cx.current_expansion.mark, def); + cx.resolver.add_macro(cx.current_expansion.mark, def, export); result } }