Auto merge of #37292 - jseyfried:import_macros_in_resolve, r=nrc

Process `#[macro_use]` imports in `resolve` and clean up macro loading

Groundwork macro modularization (cc #35896).
r? @nrc
This commit is contained in:
bors 2016-10-24 23:15:59 -07:00 committed by GitHub
commit affc3b7552
15 changed files with 493 additions and 645 deletions

View File

@ -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(),
}

View File

@ -458,8 +458,6 @@ pub struct MacroDef {
pub id: NodeId,
pub span: Span,
pub imported_from: Option<Name>,
pub export: bool,
pub use_locally: bool,
pub allow_internal_unstable: bool,
pub body: HirVec<TokenTree>,
}

View File

@ -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<ast::MacroDef>),
ProcMacros(Vec<(ast::Name, SyntaxExtension)>),
}
pub enum LoadedMacroKind {
Def(ast::MacroDef),
CustomDerive(String, Box<MultiItemModifier>),
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<LoadedMacro>;
fn process_item(&mut self, item: &ast::Item, defs: &Definitions);
fn process_item(&mut self, item: &ast::Item, defs: &Definitions, load_macros: bool)
-> Option<LoadedMacros>;
fn postprocess(&mut self, krate: &ast::Crate);
}

View File

@ -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, &macro_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, &macro_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.
}
}

View File

@ -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<PathBuf>,
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<ast::MacroDef>,
/// 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<DefIndex>,
pub svh: Svh,
pub dylib: Option<PathBuf>,
}
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 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);
}
// 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
// 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)
}
}
/// 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));
}
}
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");
}
let mut my_registrar = MyRegistrar(Vec::new());
registrar(&mut my_registrar);
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);
}
return ret
// 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<LoadedMacros> {
match item.node {
ast::ItemKind::ExternCrate(_) => {}
ast::ItemKind::ForeignMod(ref fm) => return self.process_foreign_mod(item, fm),
_ => return,
}
// 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
ast::ItemKind::ForeignMod(ref fm) => {
self.process_foreign_mod(item, fm);
return None;
}
_ => return None,
}
if let Some(info) = self.extract_crate_info(item) {
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);
}
// 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);
}
}
Some(loaded_macros)
} else {
if !info.should_link {
return;
return None;
}
None
};
let (cnum, ..) = self.resolve_crate(
&None, &info.ident, &info.name, None, item.span, PathKind::Crate, true,
);
let (cnum, ..) = self.resolve_crate(
&None, &info.ident, &info.name, None, item.span, PathKind::Crate, true,
);
let def_id = definitions.opt_local_def_id(item.id).unwrap();
let len = definitions.def_path(def_id.index).data.len();
let def_id = definitions.opt_local_def_id(item.id).unwrap();
let len = definitions.def_path(def_id.index).data.len();
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 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);
self.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
}
}
fn load_macros(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro> {
macro_import::load_macros(self, extern_crate, allows_macros)
loaded_macros
}
}

View File

@ -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<DefIdMap<Option<CachedInlinedItem>>>,
pub defid_for_inlined_node: RefCell<NodeMap<DefId>>,
pub visible_parent_map: RefCell<DefIdMap<DefId>>,
pub used_for_derive_macro: RefCell<FnvHashSet<Ident>>,
}
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()),
}
}
@ -279,14 +276,6 @@ impl CStore {
pub fn do_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option<CrateNum> {
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 {

View File

@ -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! {

View File

@ -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 }

View File

@ -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 <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 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<token::InternedString, Span>;
enum ImportSelection {
All(Span),
Some(MacroSelection),
}
pub fn load_macros(loader: &mut CrateLoader, extern_crate: &ast::Item, allows_macros: bool)
-> Vec<LoadedMacro> {
loader.load_crate(extern_crate, allows_macros)
}
impl<'a> CrateLoader<'a> {
fn load_crate(&mut self,
extern_crate: &ast::Item,
allows_macros: bool) -> Vec<LoadedMacro> {
// 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<LoadedMacro> {
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, &macros, 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<LoadedMacro>) {
// 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(&macros.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<LoadedMacro>, 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);
}
}

View File

@ -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<Span>,
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 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");
}
}
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)
};
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);
}
}
}
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);
// 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> {

View File

@ -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.

View File

@ -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);
}

View File

@ -2012,8 +2012,6 @@ pub struct MacroDef {
pub id: NodeId,
pub span: Span,
pub imported_from: Option<Ident>,
pub export: bool,
pub use_locally: bool,
pub allow_internal_unstable: bool,
pub body: Vec<TokenTree>,
}

View File

@ -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<SyntaxExtension>);
fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>);
@ -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<SyntaxExtension>) {}
fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec<Mark>) {}

View File

@ -157,14 +157,13 @@ impl IdentMacroExpander for MacroRulesExpander {
tts: Vec<tokenstream::TokenTree>,
attrs: Vec<ast::Attribute>)
-> Box<MacResult> {
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
}
}