]> git.proxmox.com Git - rustc.git/blob - src/librustc_resolve/macros.rs
New upstream version 1.13.0+dfsg1
[rustc.git] / src / librustc_resolve / macros.rs
1 // Copyright 2016 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 use Resolver;
12 use rustc::middle::cstore::LoadedMacro;
13 use rustc::util::nodemap::FnvHashMap;
14 use std::cell::RefCell;
15 use std::mem;
16 use std::rc::Rc;
17 use syntax::ast::{self, Name};
18 use syntax::errors::DiagnosticBuilder;
19 use syntax::ext::base::{self, MultiModifier, MultiDecorator, MultiItemModifier};
20 use syntax::ext::base::{NormalTT, Resolver as SyntaxResolver, SyntaxExtension};
21 use syntax::ext::expand::{Expansion, Invocation, InvocationKind};
22 use syntax::ext::hygiene::Mark;
23 use syntax::ext::tt::macro_rules;
24 use syntax::feature_gate::{self, emit_feature_err};
25 use syntax::parse::token::{self, intern};
26 use syntax::util::lev_distance::find_best_match_for_name;
27 use syntax::visit::{self, Visitor};
28 use syntax_pos::Span;
29
30 #[derive(Clone, Default)]
31 pub struct ExpansionData {
32 module: Rc<ModuleData>,
33 }
34
35 // FIXME(jseyfried): merge with `::ModuleS`.
36 #[derive(Default)]
37 struct ModuleData {
38 parent: Option<Rc<ModuleData>>,
39 macros: RefCell<FnvHashMap<Name, Rc<SyntaxExtension>>>,
40 macros_escape: bool,
41 }
42
43 impl<'a> base::Resolver for Resolver<'a> {
44 fn next_node_id(&mut self) -> ast::NodeId {
45 self.session.next_node_id()
46 }
47
48 fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) {
49 expansion.visit_with(&mut ExpansionVisitor {
50 current_module: self.expansion_data[&mark.as_u32()].module.clone(),
51 resolver: self,
52 });
53 }
54
55 fn add_macro(&mut self, scope: Mark, mut def: ast::MacroDef) {
56 if &def.ident.name.as_str() == "macro_rules" {
57 self.session.span_err(def.span, "user-defined macros may not be named `macro_rules`");
58 }
59 if def.use_locally {
60 let ext = macro_rules::compile(&self.session.parse_sess, &def);
61 self.add_ext(scope, def.ident, Rc::new(ext));
62 }
63 if def.export {
64 def.id = self.next_node_id();
65 self.exported_macros.push(def);
66 }
67 }
68
69 fn add_ext(&mut self, scope: Mark, ident: ast::Ident, ext: Rc<SyntaxExtension>) {
70 if let NormalTT(..) = *ext {
71 self.macro_names.insert(ident.name);
72 }
73
74 let mut module = self.expansion_data[&scope.as_u32()].module.clone();
75 while module.macros_escape {
76 module = module.parent.clone().unwrap();
77 }
78 module.macros.borrow_mut().insert(ident.name, ext);
79 }
80
81 fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>) {
82 self.macros_at_scope.insert(id, macros);
83 }
84
85 fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
86 for i in 0..attrs.len() {
87 let name = intern(&attrs[i].name());
88 match self.expansion_data[&0].module.macros.borrow().get(&name) {
89 Some(ext) => match **ext {
90 MultiModifier(..) | MultiDecorator(..) | SyntaxExtension::AttrProcMacro(..) => {
91 return Some(attrs.remove(i))
92 }
93 _ => {}
94 },
95 None => {}
96 }
97 }
98 None
99 }
100
101 fn resolve_invoc(&mut self, scope: Mark, invoc: &Invocation) -> Option<Rc<SyntaxExtension>> {
102 let (name, span) = match invoc.kind {
103 InvocationKind::Bang { ref mac, .. } => {
104 let path = &mac.node.path;
105 if path.segments.len() > 1 || path.global ||
106 !path.segments[0].parameters.is_empty() {
107 self.session.span_err(path.span,
108 "expected macro name without module separators");
109 return None;
110 }
111 (path.segments[0].identifier.name, path.span)
112 }
113 InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span),
114 };
115
116 let mut module = self.expansion_data[&scope.as_u32()].module.clone();
117 loop {
118 if let Some(ext) = module.macros.borrow().get(&name) {
119 return Some(ext.clone());
120 }
121 match module.parent.clone() {
122 Some(parent) => module = parent,
123 None => break,
124 }
125 }
126
127 let mut err =
128 self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name));
129 self.suggest_macro_name(&name.as_str(), &mut err);
130 err.emit();
131 None
132 }
133
134 fn resolve_derive_mode(&mut self, ident: ast::Ident) -> Option<Rc<MultiItemModifier>> {
135 self.derive_modes.get(&ident.name).cloned()
136 }
137 }
138
139 impl<'a> Resolver<'a> {
140 fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) {
141 if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) {
142 if suggestion != name {
143 err.help(&format!("did you mean `{}!`?", suggestion));
144 } else {
145 err.help(&format!("have you added the `#[macro_use]` on the module/import?"));
146 }
147 }
148 }
149
150 fn insert_custom_derive(&mut self, name: &str, ext: Rc<MultiItemModifier>, sp: Span) {
151 if !self.session.features.borrow().rustc_macro {
152 let sess = &self.session.parse_sess;
153 let msg = "loading custom derive macro crates is experimentally supported";
154 emit_feature_err(sess, "rustc_macro", sp, feature_gate::GateIssue::Language, msg);
155 }
156 if self.derive_modes.insert(token::intern(name), ext).is_some() {
157 self.session.span_err(sp, &format!("cannot shadow existing derive mode `{}`", name));
158 }
159 }
160 }
161
162 struct ExpansionVisitor<'b, 'a: 'b> {
163 resolver: &'b mut Resolver<'a>,
164 current_module: Rc<ModuleData>,
165 }
166
167 impl<'a, 'b> ExpansionVisitor<'a, 'b> {
168 fn visit_invoc(&mut self, id: ast::NodeId) {
169 self.resolver.expansion_data.insert(id.as_u32(), ExpansionData {
170 module: self.current_module.clone(),
171 });
172 }
173
174 // does this attribute list contain "macro_use"?
175 fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
176 for attr in attrs {
177 if attr.check_name("macro_escape") {
178 let msg = "macro_escape is a deprecated synonym for macro_use";
179 let mut err = self.resolver.session.struct_span_warn(attr.span, msg);
180 if let ast::AttrStyle::Inner = attr.node.style {
181 err.help("consider an outer attribute, #[macro_use] mod ...").emit();
182 } else {
183 err.emit();
184 }
185 } else if !attr.check_name("macro_use") {
186 continue;
187 }
188
189 if !attr.is_word() {
190 self.resolver.session.span_err(attr.span,
191 "arguments to macro_use are not allowed here");
192 }
193 return true;
194 }
195
196 false
197 }
198 }
199
200 macro_rules! method {
201 ($visit:ident: $ty:ty, $invoc:path, $walk:ident) => {
202 fn $visit(&mut self, node: &$ty) {
203 match node.node {
204 $invoc(..) => self.visit_invoc(node.id),
205 _ => visit::$walk(self, node),
206 }
207 }
208 }
209 }
210
211 impl<'a, 'b> Visitor for ExpansionVisitor<'a, 'b> {
212 method!(visit_trait_item: ast::TraitItem, ast::TraitItemKind::Macro, walk_trait_item);
213 method!(visit_impl_item: ast::ImplItem, ast::ImplItemKind::Macro, walk_impl_item);
214 method!(visit_stmt: ast::Stmt, ast::StmtKind::Mac, walk_stmt);
215 method!(visit_expr: ast::Expr, ast::ExprKind::Mac, walk_expr);
216 method!(visit_pat: ast::Pat, ast::PatKind::Mac, walk_pat);
217 method!(visit_ty: ast::Ty, ast::TyKind::Mac, walk_ty);
218
219 fn visit_item(&mut self, item: &ast::Item) {
220 match item.node {
221 ast::ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => {} // Scope placeholder
222 ast::ItemKind::Mac(..) => self.visit_invoc(item.id),
223 ast::ItemKind::Mod(..) => {
224 let module_data = ModuleData {
225 parent: Some(self.current_module.clone()),
226 macros: RefCell::new(FnvHashMap()),
227 macros_escape: self.contains_macro_use(&item.attrs),
228 };
229 let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data));
230 visit::walk_item(self, item);
231 self.current_module = orig_module;
232 }
233 ast::ItemKind::ExternCrate(..) => {
234 // We need to error on `#[macro_use] extern crate` when it isn't at the
235 // crate root, because `$crate` won't work properly.
236 // FIXME(jseyfried): This will be nicer once `ModuleData` is merged with `ModuleS`.
237 let is_crate_root = self.current_module.parent.as_ref().unwrap().parent.is_none();
238 for def in self.resolver.crate_loader.load_macros(item, is_crate_root) {
239 match def {
240 LoadedMacro::Def(def) => self.resolver.add_macro(Mark::root(), def),
241 LoadedMacro::CustomDerive(name, ext) => {
242 self.resolver.insert_custom_derive(&name, ext, item.span);
243 }
244 }
245 }
246 visit::walk_item(self, item);
247 }
248 _ => visit::walk_item(self, item),
249 }
250 }
251
252 fn visit_block(&mut self, block: &ast::Block) {
253 let module_data = ModuleData {
254 parent: Some(self.current_module.clone()),
255 macros: RefCell::new(FnvHashMap()),
256 macros_escape: false,
257 };
258 let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data));
259 visit::walk_block(self, block);
260 self.current_module = orig_module;
261 }
262 }