]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/macro_import.rs
Rollup merge of #31031 - brson:issue-30123, r=nikomatsakis
[rust.git] / src / librustc_metadata / macro_import.rs
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.
4 //
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.
10
11 //! Used by `rustc` when loading a crate with exported macros.
12
13 use creader::CrateReader;
14 use cstore::CStore;
15
16 use rustc::session::Session;
17
18 use std::collections::{HashSet, HashMap};
19 use syntax::codemap::Span;
20 use syntax::parse::token;
21 use syntax::ast;
22 use syntax::attr;
23 use syntax::visit;
24 use syntax::visit::Visitor;
25 use syntax::attr::AttrMetaMethods;
26
27 struct MacroLoader<'a> {
28     sess: &'a Session,
29     span_whitelist: HashSet<Span>,
30     reader: CrateReader<'a>,
31     macros: Vec<ast::MacroDef>,
32 }
33
34 impl<'a> MacroLoader<'a> {
35     fn new(sess: &'a Session, cstore: &'a CStore) -> MacroLoader<'a> {
36         MacroLoader {
37             sess: sess,
38             span_whitelist: HashSet::new(),
39             reader: CrateReader::new(sess, cstore),
40             macros: vec![],
41         }
42     }
43 }
44
45 pub fn call_bad_macro_reexport(a: &Session, b: Span) {
46     span_err!(a, b, E0467, "bad macro reexport");
47 }
48
49 /// Read exported macros.
50 pub fn read_macro_defs(sess: &Session, cstore: &CStore, krate: &ast::Crate)
51                        -> Vec<ast::MacroDef>
52 {
53     let mut loader = MacroLoader::new(sess, cstore);
54
55     // We need to error on `#[macro_use] extern crate` when it isn't at the
56     // crate root, because `$crate` won't work properly. Identify these by
57     // spans, because the crate map isn't set up yet.
58     for item in &krate.module.items {
59         if let ast::ItemExternCrate(_) = item.node {
60             loader.span_whitelist.insert(item.span);
61         }
62     }
63
64     visit::walk_crate(&mut loader, krate);
65
66     loader.macros
67 }
68
69 pub type MacroSelection = HashMap<token::InternedString, Span>;
70
71 // note that macros aren't expanded yet, and therefore macros can't add macro imports.
72 impl<'a, 'v> Visitor<'v> for MacroLoader<'a> {
73     fn visit_item(&mut self, item: &ast::Item) {
74         // We're only interested in `extern crate`.
75         match item.node {
76             ast::ItemExternCrate(_) => {}
77             _ => {
78                 visit::walk_item(self, item);
79                 return;
80             }
81         }
82
83         // Parse the attributes relating to macros.
84         let mut import = Some(HashMap::new());  // None => load all
85         let mut reexport = HashMap::new();
86
87         for attr in &item.attrs {
88             let mut used = true;
89             match &attr.name()[..] {
90                 "macro_use" => {
91                     let names = attr.meta_item_list();
92                     if names.is_none() {
93                         // no names => load all
94                         import = None;
95                     }
96                     if let (Some(sel), Some(names)) = (import.as_mut(), names) {
97                         for attr in names {
98                             if let ast::MetaWord(ref name) = attr.node {
99                                 sel.insert(name.clone(), attr.span);
100                             } else {
101                                 span_err!(self.sess, attr.span, E0466, "bad macro import");
102                             }
103                         }
104                     }
105                 }
106                 "macro_reexport" => {
107                     let names = match attr.meta_item_list() {
108                         Some(names) => names,
109                         None => {
110                             call_bad_macro_reexport(self.sess, attr.span);
111                             continue;
112                         }
113                     };
114
115                     for attr in names {
116                         if let ast::MetaWord(ref name) = attr.node {
117                             reexport.insert(name.clone(), attr.span);
118                         } else {
119                             call_bad_macro_reexport(self.sess, attr.span);
120                         }
121                     }
122                 }
123                 _ => used = false,
124             }
125             if used {
126                 attr::mark_used(attr);
127             }
128         }
129
130         self.load_macros(item, import, reexport)
131     }
132
133     fn visit_mac(&mut self, _: &ast::Mac) {
134         // bummer... can't see macro imports inside macros.
135         // do nothing.
136     }
137 }
138
139 impl<'a> MacroLoader<'a> {
140     fn load_macros<'b>(&mut self,
141                        vi: &ast::Item,
142                        import: Option<MacroSelection>,
143                        reexport: MacroSelection) {
144         if let Some(sel) = import.as_ref() {
145             if sel.is_empty() && reexport.is_empty() {
146                 return;
147             }
148         }
149
150         if !self.span_whitelist.contains(&vi.span) {
151             span_err!(self.sess, vi.span, E0468,
152                       "an `extern crate` loading macros must be at the crate root");
153             return;
154         }
155
156         let macros = self.reader.read_exported_macros(vi);
157         let mut seen = HashSet::new();
158
159         for mut def in macros {
160             let name = def.ident.name.as_str();
161
162             def.use_locally = match import.as_ref() {
163                 None => true,
164                 Some(sel) => sel.contains_key(&name),
165             };
166             def.export = reexport.contains_key(&name);
167             def.allow_internal_unstable = attr::contains_name(&def.attrs,
168                                                               "allow_internal_unstable");
169             debug!("load_macros: loaded: {:?}", def);
170             self.macros.push(def);
171             seen.insert(name);
172         }
173
174         if let Some(sel) = import.as_ref() {
175             for (name, span) in sel {
176                 if !seen.contains(&name) {
177                     span_err!(self.sess, *span, E0469,
178                               "imported macro not found");
179                 }
180             }
181         }
182
183         for (name, span) in &reexport {
184             if !seen.contains(&name) {
185                 span_err!(self.sess, *span, E0470,
186                           "reexported macro not found");
187             }
188         }
189     }
190 }