]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
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::ast; | |
18 | use syntax::attr; | |
19 | use syntax::codemap::Span; | |
20 | use syntax::parse::token; | |
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; | |
c34b1796 | 81 | match &attr.name()[..] { |
85aaf69f SL |
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 = token::get_ident(def.ident); | |
153 | seen.insert(name.clone()); | |
154 | ||
155 | def.use_locally = match import.as_ref() { | |
156 | None => true, | |
157 | Some(sel) => sel.contains_key(&name), | |
158 | }; | |
159 | def.export = reexport.contains_key(&name); | |
c34b1796 AL |
160 | def.allow_internal_unstable = attr::contains_name(&def.attrs, |
161 | "allow_internal_unstable"); | |
162 | debug!("load_macros: loaded: {:?}", def); | |
85aaf69f SL |
163 | self.macros.push(def); |
164 | } | |
165 | ||
166 | if let Some(sel) = import.as_ref() { | |
62682a34 | 167 | for (name, span) in sel { |
85aaf69f SL |
168 | if !seen.contains(name) { |
169 | self.sess.span_err(*span, "imported macro not found"); | |
170 | } | |
171 | } | |
172 | } | |
173 | ||
62682a34 | 174 | for (name, span) in &reexport { |
85aaf69f SL |
175 | if !seen.contains(name) { |
176 | self.sess.span_err(*span, "reexported macro not found"); | |
177 | } | |
178 | } | |
179 | } | |
180 | } |