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.
12 use rustc
::middle
::cstore
::LoadedMacro
;
13 use rustc
::util
::nodemap
::FnvHashMap
;
14 use std
::cell
::RefCell
;
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}
;
30 #[derive(Clone, Default)]
31 pub struct ExpansionData
{
32 module
: Rc
<ModuleData
>,
35 // FIXME(jseyfried): merge with `::ModuleS`.
38 parent
: Option
<Rc
<ModuleData
>>,
39 macros
: RefCell
<FnvHashMap
<Name
, Rc
<SyntaxExtension
>>>,
43 impl<'a
> base
::Resolver
for Resolver
<'a
> {
44 fn next_node_id(&mut self) -> ast
::NodeId
{
45 self.session
.next_node_id()
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(),
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`");
60 let ext
= macro_rules
::compile(&self.session
.parse_sess
, &def
);
61 self.add_ext(scope
, def
.ident
, Rc
::new(ext
));
64 def
.id
= self.next_node_id();
65 self.exported_macros
.push(def
);
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
);
74 let mut module
= self.expansion_data
[&scope
.as_u32()].module
.clone();
75 while module
.macros_escape
{
76 module
= module
.parent
.clone().unwrap();
78 module
.macros
.borrow_mut().insert(ident
.name
, ext
);
81 fn add_expansions_at_stmt(&mut self, id
: ast
::NodeId
, macros
: Vec
<Mark
>) {
82 self.macros_at_scope
.insert(id
, macros
);
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
))
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");
111 (path
.segments
[0].identifier
.name
, path
.span
)
113 InvocationKind
::Attr { ref attr, .. }
=> (intern(&*attr
.name()), attr
.span
),
116 let mut module
= self.expansion_data
[&scope
.as_u32()].module
.clone();
118 if let Some(ext
) = module
.macros
.borrow().get(&name
) {
119 return Some(ext
.clone());
121 match module
.parent
.clone() {
122 Some(parent
) => module
= parent
,
128 self.session
.struct_span_err(span
, &format
!("macro undefined: '{}!'", name
));
129 self.suggest_macro_name(&name
.as_str(), &mut err
);
134 fn resolve_derive_mode(&mut self, ident
: ast
::Ident
) -> Option
<Rc
<MultiItemModifier
>> {
135 self.derive_modes
.get(&ident
.name
).cloned()
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
));
145 err
.help(&format
!("have you added the `#[macro_use]` on the module/import?"));
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
);
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
));
162 struct ExpansionVisitor
<'b
, 'a
: 'b
> {
163 resolver
: &'b
mut Resolver
<'a
>,
164 current_module
: Rc
<ModuleData
>,
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(),
174 // does this attribute list contain "macro_use"?
175 fn contains_macro_use(&mut self, attrs
: &[ast
::Attribute
]) -> bool
{
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();
185 } else if !attr
.check_name("macro_use") {
190 self.resolver
.session
.span_err(attr
.span
,
191 "arguments to macro_use are not allowed here");
200 macro_rules
! method
{
201 ($visit
:ident
: $ty
:ty
, $invoc
:path
, $walk
:ident
) => {
202 fn $
visit(&mut self, node
: &$ty
) {
204 $
invoc(..) => self.visit_invoc(node
.id
),
205 _
=> visit
::$
walk(self, node
),
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
);
219 fn visit_item(&mut self, item
: &ast
::Item
) {
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
),
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
;
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
) {
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
);
246 visit
::walk_item(self, item
);
248 _
=> visit
::walk_item(self, item
),
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,
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
;