1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Used by `rustc` when loading a crate with exported macros.
14 use metadata::creader::CrateReader;
16 use std::collections::{HashSet, HashMap};
17 use syntax::codemap::Span;
18 use syntax::parse::token;
22 use syntax::visit::Visitor;
23 use syntax::attr::AttrMetaMethods;
24 use rustc_front::attr::AttrMetaMethods as FrontAttrMetaMethods;
26 struct MacroLoader<'a> {
28 span_whitelist: HashSet<Span>,
29 reader: CrateReader<'a>,
30 macros: Vec<ast::MacroDef>,
33 impl<'a> MacroLoader<'a> {
34 fn new(sess: &'a Session) -> MacroLoader<'a> {
37 span_whitelist: HashSet::new(),
38 reader: CrateReader::new(sess),
44 pub fn call_bad_macro_reexport(a: &Session, b: Span) {
45 span_err!(a, b, E0467, "bad macro reexport");
48 /// Read exported macros.
49 pub fn read_macro_defs(sess: &Session, krate: &ast::Crate) -> Vec<ast::MacroDef> {
50 let mut loader = MacroLoader::new(sess);
52 // We need to error on `#[macro_use] extern crate` when it isn't at the
53 // crate root, because `$crate` won't work properly. Identify these by
54 // spans, because the crate map isn't set up yet.
55 for item in &krate.module.items {
56 if let ast::ItemExternCrate(_) = item.node {
57 loader.span_whitelist.insert(item.span);
61 visit::walk_crate(&mut loader, krate);
66 pub type MacroSelection = HashMap<token::InternedString, Span>;
68 // note that macros aren't expanded yet, and therefore macros can't add macro imports.
69 impl<'a, 'v> Visitor<'v> for MacroLoader<'a> {
70 fn visit_item(&mut self, item: &ast::Item) {
71 // We're only interested in `extern crate`.
73 ast::ItemExternCrate(_) => {}
75 visit::walk_item(self, item);
80 // Parse the attributes relating to macros.
81 let mut import = Some(HashMap::new()); // None => load all
82 let mut reexport = HashMap::new();
84 for attr in &item.attrs {
86 match &attr.name()[..] {
88 let names = attr.meta_item_list();
90 // no names => load all
93 if let (Some(sel), Some(names)) = (import.as_mut(), names) {
95 if let ast::MetaWord(ref name) = attr.node {
96 sel.insert(name.clone(), attr.span);
98 span_err!(self.sess, attr.span, E0466, "bad macro import");
103 "macro_reexport" => {
104 let names = match attr.meta_item_list() {
105 Some(names) => names,
107 call_bad_macro_reexport(self.sess, attr.span);
113 if let ast::MetaWord(ref name) = attr.node {
114 reexport.insert(name.clone(), attr.span);
116 call_bad_macro_reexport(self.sess, attr.span);
123 attr::mark_used(attr);
127 self.load_macros(item, import, reexport)
130 fn visit_mac(&mut self, _: &ast::Mac) {
131 // bummer... can't see macro imports inside macros.
136 impl<'a> MacroLoader<'a> {
137 fn load_macros<'b>(&mut self,
139 import: Option<MacroSelection>,
140 reexport: MacroSelection) {
141 if let Some(sel) = import.as_ref() {
142 if sel.is_empty() && reexport.is_empty() {
147 if !self.span_whitelist.contains(&vi.span) {
148 span_err!(self.sess, vi.span, E0468,
149 "an `extern crate` loading macros must be at the crate root");
153 let macros = self.reader.read_exported_macros(vi);
154 let mut seen = HashSet::new();
156 for mut def in macros {
157 let name = def.ident.name.as_str();
159 def.use_locally = match import.as_ref() {
161 Some(sel) => sel.contains_key(&name),
163 def.export = reexport.contains_key(&name);
164 def.allow_internal_unstable = attr::contains_name(&def.attrs,
165 "allow_internal_unstable");
166 debug!("load_macros: loaded: {:?}", def);
167 self.macros.push(def);
171 if let Some(sel) = import.as_ref() {
172 for (name, span) in sel {
173 if !seen.contains(&name) {
174 span_err!(self.sess, *span, E0469,
175 "imported macro not found");
180 for (name, span) in &reexport {
181 if !seen.contains(&name) {
182 span_err!(self.sess, *span, E0470,
183 "reexported macro not found");