]> git.lizzy.rs Git - rust.git/blob - src/librustc/metadata/macro_import.rs
Add part of new error codes in librustc
[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 session::Session;
14 use metadata::creader::CrateReader;
15
16 use std::collections::{HashSet, HashMap};
17 use syntax::codemap::Span;
18 use syntax::parse::token;
19 use syntax::ast;
20 use syntax::attr;
21 use syntax::visit;
22 use syntax::visit::Visitor;
23 use syntax::attr::AttrMetaMethods;
24 use rustc_front::attr::AttrMetaMethods as FrontAttrMetaMethods;
25
26 struct MacroLoader<'a> {
27     sess: &'a Session,
28     span_whitelist: HashSet<Span>,
29     reader: CrateReader<'a>,
30     macros: Vec<ast::MacroDef>,
31 }
32
33 impl<'a> MacroLoader<'a> {
34     fn new(sess: &'a Session) -> MacroLoader<'a> {
35         MacroLoader {
36             sess: sess,
37             span_whitelist: HashSet::new(),
38             reader: CrateReader::new(sess),
39             macros: vec![],
40         }
41     }
42 }
43
44 pub fn call_bad_macro_reexport(a: &Session, b: Span) {
45     span_err!(a, b, E0467, "bad macro reexport");
46 }
47
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);
51
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);
58         }
59     }
60
61     visit::walk_crate(&mut loader, krate);
62
63     loader.macros
64 }
65
66 pub type MacroSelection = HashMap<token::InternedString, Span>;
67
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`.
72         match item.node {
73             ast::ItemExternCrate(_) => {}
74             _ => {
75                 visit::walk_item(self, item);
76                 return;
77             }
78         }
79
80         // Parse the attributes relating to macros.
81         let mut import = Some(HashMap::new());  // None => load all
82         let mut reexport = HashMap::new();
83
84         for attr in &item.attrs {
85             let mut used = true;
86             match &attr.name()[..] {
87                 "macro_use" => {
88                     let names = attr.meta_item_list();
89                     if names.is_none() {
90                         // no names => load all
91                         import = None;
92                     }
93                     if let (Some(sel), Some(names)) = (import.as_mut(), names) {
94                         for attr in names {
95                             if let ast::MetaWord(ref name) = attr.node {
96                                 sel.insert(name.clone(), attr.span);
97                             } else {
98                                 span_err!(self.sess, attr.span, E0466, "bad macro import");
99                             }
100                         }
101                     }
102                 }
103                 "macro_reexport" => {
104                     let names = match attr.meta_item_list() {
105                         Some(names) => names,
106                         None => {
107                             call_bad_macro_reexport(self.sess, attr.span);
108                             continue;
109                         }
110                     };
111
112                     for attr in names {
113                         if let ast::MetaWord(ref name) = attr.node {
114                             reexport.insert(name.clone(), attr.span);
115                         } else {
116                             call_bad_macro_reexport(self.sess, attr.span);
117                         }
118                     }
119                 }
120                 _ => used = false,
121             }
122             if used {
123                 attr::mark_used(attr);
124             }
125         }
126
127         self.load_macros(item, import, reexport)
128     }
129
130     fn visit_mac(&mut self, _: &ast::Mac) {
131         // bummer... can't see macro imports inside macros.
132         // do nothing.
133     }
134 }
135
136 impl<'a> MacroLoader<'a> {
137     fn load_macros<'b>(&mut self,
138                        vi: &ast::Item,
139                        import: Option<MacroSelection>,
140                        reexport: MacroSelection) {
141         if let Some(sel) = import.as_ref() {
142             if sel.is_empty() && reexport.is_empty() {
143                 return;
144             }
145         }
146
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");
150             return;
151         }
152
153         let macros = self.reader.read_exported_macros(vi);
154         let mut seen = HashSet::new();
155
156         for mut def in macros {
157             let name = def.ident.name.as_str();
158
159             def.use_locally = match import.as_ref() {
160                 None => true,
161                 Some(sel) => sel.contains_key(&name),
162             };
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);
168             seen.insert(name);
169         }
170
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");
176                 }
177             }
178         }
179
180         for (name, span) in &reexport {
181             if !seen.contains(&name) {
182                 span_err!(self.sess, *span, E0470,
183                           "reexported macro not found");
184             }
185         }
186     }
187 }