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