]> git.proxmox.com Git - rustc.git/blob - src/librustc_resolve/macros.rs
New upstream version 1.14.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 {Module, Resolver};
12 use build_reduced_graph::BuildReducedGraphVisitor;
13 use rustc::hir::def_id::{CRATE_DEF_INDEX, DefIndex};
14 use rustc::hir::map::{self, DefCollector};
15 use std::cell::Cell;
16 use std::rc::Rc;
17 use syntax::ast;
18 use syntax::errors::DiagnosticBuilder;
19 use syntax::ext::base::{self, Determinacy, MultiModifier, MultiDecorator};
20 use syntax::ext::base::{NormalTT, SyntaxExtension};
21 use syntax::ext::expand::Expansion;
22 use syntax::ext::hygiene::Mark;
23 use syntax::ext::tt::macro_rules;
24 use syntax::parse::token::intern;
25 use syntax::util::lev_distance::find_best_match_for_name;
26 use syntax_pos::Span;
27
28 #[derive(Clone)]
29 pub struct InvocationData<'a> {
30 pub module: Cell<Module<'a>>,
31 pub def_index: DefIndex,
32 // True if this expansion is in a `const_integer` position, for example `[u32; m!()]`.
33 // c.f. `DefCollector::visit_ast_const_integer`.
34 pub const_integer: bool,
35 // The scope in which the invocation path is resolved.
36 pub legacy_scope: Cell<LegacyScope<'a>>,
37 // The smallest scope that includes this invocation's expansion,
38 // or `Empty` if this invocation has not been expanded yet.
39 pub expansion: Cell<LegacyScope<'a>>,
40 }
41
42 impl<'a> InvocationData<'a> {
43 pub fn root(graph_root: Module<'a>) -> Self {
44 InvocationData {
45 module: Cell::new(graph_root),
46 def_index: CRATE_DEF_INDEX,
47 const_integer: false,
48 legacy_scope: Cell::new(LegacyScope::Empty),
49 expansion: Cell::new(LegacyScope::Empty),
50 }
51 }
52 }
53
54 #[derive(Copy, Clone)]
55 pub enum LegacyScope<'a> {
56 Empty,
57 Invocation(&'a InvocationData<'a>), // The scope of the invocation, not including its expansion
58 Expansion(&'a InvocationData<'a>), // The scope of the invocation, including its expansion
59 Binding(&'a LegacyBinding<'a>),
60 }
61
62 impl<'a> LegacyScope<'a> {
63 fn simplify_expansion(mut invoc: &'a InvocationData<'a>) -> Self {
64 while let LegacyScope::Invocation(_) = invoc.expansion.get() {
65 match invoc.legacy_scope.get() {
66 LegacyScope::Expansion(new_invoc) => invoc = new_invoc,
67 LegacyScope::Binding(_) => break,
68 scope @ _ => return scope,
69 }
70 }
71 LegacyScope::Expansion(invoc)
72 }
73 }
74
75 pub struct LegacyBinding<'a> {
76 pub parent: LegacyScope<'a>,
77 pub name: ast::Name,
78 ext: Rc<SyntaxExtension>,
79 pub span: Span,
80 }
81
82 impl<'a> base::Resolver for Resolver<'a> {
83 fn next_node_id(&mut self) -> ast::NodeId {
84 self.session.next_node_id()
85 }
86
87 fn get_module_scope(&mut self, id: ast::NodeId) -> Mark {
88 let mark = Mark::fresh();
89 let module = self.module_map[&id];
90 self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData {
91 module: Cell::new(module),
92 def_index: module.def_id().unwrap().index,
93 const_integer: false,
94 legacy_scope: Cell::new(LegacyScope::Empty),
95 expansion: Cell::new(LegacyScope::Empty),
96 }));
97 mark
98 }
99
100 fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) {
101 let invocation = self.invocations[&mark];
102 self.collect_def_ids(invocation, expansion);
103
104 self.current_module = invocation.module.get();
105 let mut visitor = BuildReducedGraphVisitor {
106 resolver: self,
107 legacy_scope: LegacyScope::Invocation(invocation),
108 expansion: mark,
109 };
110 expansion.visit_with(&mut visitor);
111 invocation.expansion.set(visitor.legacy_scope);
112 }
113
114 fn add_macro(&mut self, scope: Mark, mut def: ast::MacroDef, export: bool) {
115 if &def.ident.name.as_str() == "macro_rules" {
116 self.session.span_err(def.span, "user-defined macros may not be named `macro_rules`");
117 }
118
119 let invocation = self.invocations[&scope];
120 let binding = self.arenas.alloc_legacy_binding(LegacyBinding {
121 parent: invocation.legacy_scope.get(),
122 name: def.ident.name,
123 ext: Rc::new(macro_rules::compile(&self.session.parse_sess, &def)),
124 span: def.span,
125 });
126 invocation.legacy_scope.set(LegacyScope::Binding(binding));
127 self.macro_names.insert(def.ident.name);
128
129 if export {
130 def.id = self.next_node_id();
131 self.exported_macros.push(def);
132 }
133 }
134
135 fn add_ext(&mut self, ident: ast::Ident, ext: Rc<SyntaxExtension>) {
136 if let NormalTT(..) = *ext {
137 self.macro_names.insert(ident.name);
138 }
139 self.builtin_macros.insert(ident.name, ext);
140 }
141
142 fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>) {
143 self.macros_at_scope.insert(id, macros);
144 }
145
146 fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
147 for i in 0..attrs.len() {
148 let name = intern(&attrs[i].name());
149 match self.builtin_macros.get(&name) {
150 Some(ext) => match **ext {
151 MultiModifier(..) | MultiDecorator(..) | SyntaxExtension::AttrProcMacro(..) => {
152 return Some(attrs.remove(i))
153 }
154 _ => {}
155 },
156 None => {}
157 }
158 }
159 None
160 }
161
162 fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
163 -> Result<Rc<SyntaxExtension>, Determinacy> {
164 if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
165 self.session.span_err(path.span, "expected macro name without module separators");
166 return Err(Determinacy::Determined);
167 }
168 let name = path.segments[0].identifier.name;
169
170 let invocation = self.invocations[&scope];
171 if let LegacyScope::Expansion(parent) = invocation.legacy_scope.get() {
172 invocation.legacy_scope.set(LegacyScope::simplify_expansion(parent));
173 }
174 self.resolve_macro_name(invocation.legacy_scope.get(), name).ok_or_else(|| {
175 if force {
176 let msg = format!("macro undefined: '{}!'", name);
177 let mut err = self.session.struct_span_err(path.span, &msg);
178 self.suggest_macro_name(&name.as_str(), &mut err);
179 err.emit();
180 Determinacy::Determined
181 } else {
182 Determinacy::Undetermined
183 }
184 })
185 }
186 }
187
188 impl<'a> Resolver<'a> {
189 pub fn resolve_macro_name(&mut self, mut scope: LegacyScope<'a>, name: ast::Name)
190 -> Option<Rc<SyntaxExtension>> {
191 let mut possible_time_travel = None;
192 let mut relative_depth: u32 = 0;
193 loop {
194 scope = match scope {
195 LegacyScope::Empty => break,
196 LegacyScope::Expansion(invocation) => {
197 if let LegacyScope::Empty = invocation.expansion.get() {
198 if possible_time_travel.is_none() {
199 possible_time_travel = Some(scope);
200 }
201 invocation.legacy_scope.get()
202 } else {
203 relative_depth += 1;
204 invocation.expansion.get()
205 }
206 }
207 LegacyScope::Invocation(invocation) => {
208 relative_depth = relative_depth.saturating_sub(1);
209 invocation.legacy_scope.get()
210 }
211 LegacyScope::Binding(binding) => {
212 if binding.name == name {
213 if let Some(scope) = possible_time_travel {
214 // Check for disallowed shadowing later
215 self.lexical_macro_resolutions.push((name, scope));
216 } else if relative_depth > 0 {
217 self.disallowed_shadowing.push(binding);
218 }
219 return Some(binding.ext.clone());
220 }
221 binding.parent
222 }
223 };
224 }
225
226 if let Some(scope) = possible_time_travel {
227 self.lexical_macro_resolutions.push((name, scope));
228 }
229 self.builtin_macros.get(&name).cloned()
230 }
231
232 fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) {
233 if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) {
234 if suggestion != name {
235 err.help(&format!("did you mean `{}!`?", suggestion));
236 } else {
237 err.help(&format!("have you added the `#[macro_use]` on the module/import?"));
238 }
239 }
240 }
241
242 fn collect_def_ids(&mut self, invocation: &'a InvocationData<'a>, expansion: &Expansion) {
243 let Resolver { ref mut invocations, arenas, graph_root, .. } = *self;
244 let InvocationData { def_index, const_integer, .. } = *invocation;
245
246 let visit_macro_invoc = &mut |invoc: map::MacroInvocationData| {
247 invocations.entry(invoc.mark).or_insert_with(|| {
248 arenas.alloc_invocation_data(InvocationData {
249 def_index: invoc.def_index,
250 const_integer: invoc.const_integer,
251 module: Cell::new(graph_root),
252 expansion: Cell::new(LegacyScope::Empty),
253 legacy_scope: Cell::new(LegacyScope::Empty),
254 })
255 });
256 };
257
258 let mut def_collector = DefCollector::new(&mut self.definitions);
259 def_collector.visit_macro_invoc = Some(visit_macro_invoc);
260 def_collector.with_parent(def_index, |def_collector| {
261 if const_integer {
262 if let Expansion::Expr(ref expr) = *expansion {
263 def_collector.visit_ast_const_integer(expr);
264 }
265 }
266 expansion.visit_with(def_collector)
267 });
268 }
269 }