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