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.
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.
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}
;
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
;
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
>>,
42 impl<'a
> InvocationData
<'a
> {
43 pub fn root(graph_root
: Module
<'a
>) -> Self {
45 module
: Cell
::new(graph_root
),
46 def_index
: CRATE_DEF_INDEX
,
48 legacy_scope
: Cell
::new(LegacyScope
::Empty
),
49 expansion
: Cell
::new(LegacyScope
::Empty
),
54 #[derive(Copy, Clone)]
55 pub enum LegacyScope
<'a
> {
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
>),
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
,
71 LegacyScope
::Expansion(invoc
)
75 pub struct LegacyBinding
<'a
> {
76 pub parent
: LegacyScope
<'a
>,
78 ext
: Rc
<SyntaxExtension
>,
82 impl<'a
> base
::Resolver
for Resolver
<'a
> {
83 fn next_node_id(&mut self) -> ast
::NodeId
{
84 self.session
.next_node_id()
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
,
94 legacy_scope
: Cell
::new(LegacyScope
::Empty
),
95 expansion
: Cell
::new(LegacyScope
::Empty
),
100 fn visit_expansion(&mut self, mark
: Mark
, expansion
: &Expansion
) {
101 let invocation
= self.invocations
[&mark
];
102 self.collect_def_ids(invocation
, expansion
);
104 self.current_module
= invocation
.module
.get();
105 let mut visitor
= BuildReducedGraphVisitor
{
107 legacy_scope
: LegacyScope
::Invocation(invocation
),
110 expansion
.visit_with(&mut visitor
);
111 invocation
.expansion
.set(visitor
.legacy_scope
);
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`");
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
)),
126 invocation
.legacy_scope
.set(LegacyScope
::Binding(binding
));
127 self.macro_names
.insert(def
.ident
.name
);
130 def
.id
= self.next_node_id();
131 self.exported_macros
.push(def
);
135 fn add_ext(&mut self, ident
: ast
::Ident
, ext
: Rc
<SyntaxExtension
>) {
136 if let NormalTT(..) = *ext
{
137 self.macro_names
.insert(ident
.name
);
139 self.builtin_macros
.insert(ident
.name
, ext
);
142 fn add_expansions_at_stmt(&mut self, id
: ast
::NodeId
, macros
: Vec
<Mark
>) {
143 self.macros_at_scope
.insert(id
, macros
);
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
))
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
);
168 let name
= path
.segments
[0].identifier
.name
;
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
));
174 self.resolve_macro_name(invocation
.legacy_scope
.get(), name
).ok_or_else(|| {
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
);
180 Determinacy
::Determined
182 Determinacy
::Undetermined
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;
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
);
201 invocation
.legacy_scope
.get()
204 invocation
.expansion
.get()
207 LegacyScope
::Invocation(invocation
) => {
208 relative_depth
= relative_depth
.saturating_sub(1);
209 invocation
.legacy_scope
.get()
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
);
219 return Some(binding
.ext
.clone());
226 if let Some(scope
) = possible_time_travel
{
227 self.lexical_macro_resolutions
.push((name
, scope
));
229 self.builtin_macros
.get(&name
).cloned()
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
));
237 err
.help(&format
!("have you added the `#[macro_use]` on the module/import?"));
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
;
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
),
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
| {
262 if let Expansion
::Expr(ref expr
) = *expansion
{
263 def_collector
.visit_ast_const_integer(expr
);
266 expansion
.visit_with(def_collector
)