Import macros in resolve instead of in metadata::macro_import.
This commit is contained in:
parent
4845adde36
commit
e4baeaa30d
7 changed files with 411 additions and 492 deletions
|
|
@ -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,13 @@ 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 LoadedMacroKind {
|
||||
Def(ast::MacroDef),
|
||||
CustomDerive(String, Box<MultiItemModifier>),
|
||||
pub enum LoadedMacros {
|
||||
MacroRules(Vec<ast::MacroDef>),
|
||||
ProcMacros(Vec<(ast::Name, SyntaxExtension)>),
|
||||
}
|
||||
|
||||
pub trait CrateLoader {
|
||||
fn load_macros(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro>;
|
||||
fn load_macros(&mut self, extern_crate: &ast::Item) -> LoadedMacros;
|
||||
fn process_item(&mut self, item: &ast::Item, defs: &Definitions);
|
||||
fn postprocess(&mut self, krate: &ast::Crate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,11 @@
|
|||
|
||||
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;
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -591,15 +591,14 @@ impl<'a> CrateLoader<'a> {
|
|||
|
||||
ret.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()
|
||||
|
|
@ -639,6 +638,58 @@ impl<'a> CrateLoader<'a> {
|
|||
return ret
|
||||
}
|
||||
|
||||
/// 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, span: Span, macros: &Macros, index: DefIndex)
|
||||
-> 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 = 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(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));
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
/// SVH and DefIndex of the registrar function.
|
||||
pub fn find_plugin_registrar(&mut self, span: Span, name: &str)
|
||||
|
|
@ -1030,7 +1081,17 @@ impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_macros(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro> {
|
||||
macro_import::load_macros(self, extern_crate, allows_macros)
|
||||
fn load_macros(&mut self, extern_crate: &ast::Item) -> LoadedMacros {
|
||||
let macros = self.read_macros(extern_crate);
|
||||
|
||||
if let Some(index) = macros.custom_derive_registrar {
|
||||
// custom derive crates currently should not have any macro_rules!
|
||||
// exported macros, enforced elsewhere
|
||||
assert_eq!(macros.macro_rules.len(), 0);
|
||||
let custom_derives = self.load_derive_macros(extern_crate.span, ¯os, index);
|
||||
LoadedMacros::ProcMacros(custom_derives)
|
||||
} else {
|
||||
LoadedMacros::MacroRules(macros.macro_rules)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,28 @@ 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() {
|
||||
Some(self.crate_loader.load_macros(item))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
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 +234,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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -516,6 +492,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 +602,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.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue