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:
commit
affc3b7552
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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, ¯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<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(¯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<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);
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
|
|
@ -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>) {}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue