about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs69
-rw-r--r--src/librustc_resolve/lib.rs15
-rw-r--r--src/librustc_resolve/macros.rs182
3 files changed, 167 insertions, 99 deletions
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 6d9786c8109..c1c58232054 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -13,7 +13,7 @@
 //! Here we build the "reduced graph": the graph of the module tree without
 //! any imports resolved.
 
-use macros;
+use macros::{InvocationData, LegacyImports, LegacyScope};
 use resolve_imports::ImportDirectiveSubclass::{self, GlobImport};
 use {Module, ModuleS, ModuleKind};
 use Namespace::{self, TypeNS, ValueNS};
@@ -84,7 +84,7 @@ impl<'b> Resolver<'b> {
     }
 
     /// Constructs the reduced graph for one item.
-    fn build_reduced_graph_for_item(&mut self, item: &Item, expansion: Mark) {
+    fn build_reduced_graph_for_item(&mut self, item: &Item, legacy_imports: &mut LegacyImports) {
         let parent = self.current_module;
         let name = item.ident.name;
         let sp = item.span;
@@ -200,16 +200,9 @@ impl<'b> Resolver<'b> {
                         LoadedMacroKind::Def(mut def) => {
                             let name = def.ident.name;
                             if def.use_locally {
-                                let ext = macro_rules::compile(&self.session.parse_sess, &def);
-                                let shadowing =
-                                    self.resolve_macro_name(Mark::root(), name, false).is_some();
-                                self.invocations[&Mark::root()].module.get().macros.borrow_mut()
-                                    .insert(name, macros::NameBinding {
-                                        ext: Rc::new(ext),
-                                        expansion: expansion,
-                                        shadowing: shadowing,
-                                        span: loaded_macro.import_site,
-                                    });
+                                let ext =
+                                    Rc::new(macro_rules::compile(&self.session.parse_sess, &def));
+                                legacy_imports.insert(name, (ext, loaded_macro.import_site));
                                 self.macro_names.insert(name);
                             }
                             if def.export {
@@ -250,7 +243,6 @@ impl<'b> Resolver<'b> {
                         attr::contains_name(&item.attrs, "no_implicit_prelude")
                     },
                     normal_ancestor_id: Some(item.id),
-                    macros_escape: self.contains_macro_use(&item.attrs),
                     ..ModuleS::new(Some(parent), ModuleKind::Def(def, name))
                 });
                 self.define(parent, name, TypeNS, (module, sp, vis));
@@ -520,22 +512,26 @@ impl<'b> Resolver<'b> {
 
 pub struct BuildReducedGraphVisitor<'a, 'b: 'a> {
     pub resolver: &'a mut Resolver<'b>,
-    pub expansion: Mark,
+    pub legacy_scope: LegacyScope<'b>,
+    pub legacy_imports: LegacyImports,
 }
 
 impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
-    fn visit_invoc(&mut self, id: ast::NodeId) {
-        let mark = Mark::from_placeholder_id(id);
-        self.resolver.invocations[&mark].module.set(self.resolver.current_module);
+    fn visit_invoc(&mut self, id: ast::NodeId) -> &'b InvocationData<'b> {
+        let invocation = self.resolver.invocations[&Mark::from_placeholder_id(id)];
+        invocation.module.set(self.resolver.current_module);
+        invocation.legacy_scope.set(self.legacy_scope);
+        invocation
     }
 }
 
 macro_rules! method {
     ($visit:ident: $ty:ty, $invoc:path, $walk:ident) => {
         fn $visit(&mut self, node: &$ty) {
-            match node.node {
-                $invoc(..) => self.visit_invoc(node.id),
-                _ => visit::$walk(self, node),
+            if let $invoc(..) = node.node {
+                self.visit_invoc(node.id);
+            } else {
+                visit::$walk(self, node);
             }
         }
     }
@@ -543,22 +539,35 @@ macro_rules! method {
 
 impl<'a, 'b> Visitor for BuildReducedGraphVisitor<'a, 'b> {
     method!(visit_impl_item: ast::ImplItem, ast::ImplItemKind::Macro, walk_impl_item);
-    method!(visit_stmt:      ast::Stmt,     ast::StmtKind::Mac,       walk_stmt);
     method!(visit_expr:      ast::Expr,     ast::ExprKind::Mac,       walk_expr);
     method!(visit_pat:       ast::Pat,      ast::PatKind::Mac,        walk_pat);
     method!(visit_ty:        ast::Ty,       ast::TyKind::Mac,         walk_ty);
 
     fn visit_item(&mut self, item: &Item) {
-        match item.node {
+        let macro_use = match item.node {
             ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => return, // Scope placeholder
-            ItemKind::Mac(..) => return self.visit_invoc(item.id),
-            _ => {}
-        }
+            ItemKind::Mac(..) => {
+                return self.legacy_scope = LegacyScope::Expansion(self.visit_invoc(item.id));
+            }
+            ItemKind::Mod(..) => self.resolver.contains_macro_use(&item.attrs),
+            _ => false,
+        };
 
-        let parent = self.resolver.current_module;
-        self.resolver.build_reduced_graph_for_item(item, self.expansion);
+        let (parent, legacy_scope) = (self.resolver.current_module, self.legacy_scope);
+        self.resolver.build_reduced_graph_for_item(item, &mut self.legacy_imports);
         visit::walk_item(self, item);
         self.resolver.current_module = parent;
+        if !macro_use {
+            self.legacy_scope = legacy_scope;
+        }
+    }
+
+    fn visit_stmt(&mut self, stmt: &ast::Stmt) {
+        if let ast::StmtKind::Mac(..) = stmt.node {
+            self.legacy_scope = LegacyScope::Expansion(self.visit_invoc(stmt.id));
+        } else {
+            visit::walk_stmt(self, stmt);
+        }
     }
 
     fn visit_foreign_item(&mut self, foreign_item: &ForeignItem) {
@@ -567,10 +576,11 @@ impl<'a, 'b> Visitor for BuildReducedGraphVisitor<'a, 'b> {
     }
 
     fn visit_block(&mut self, block: &Block) {
-        let parent = self.resolver.current_module;
+        let (parent, legacy_scope) = (self.resolver.current_module, self.legacy_scope);
         self.resolver.build_reduced_graph_for_block(block);
         visit::walk_block(self, block);
         self.resolver.current_module = parent;
+        self.legacy_scope = legacy_scope;
     }
 
     fn visit_trait_item(&mut self, item: &TraitItem) {
@@ -578,7 +588,8 @@ impl<'a, 'b> Visitor for BuildReducedGraphVisitor<'a, 'b> {
         let def_id = parent.def_id().unwrap();
 
         if let TraitItemKind::Macro(_) = item.node {
-            return self.visit_invoc(item.id);
+            self.visit_invoc(item.id);
+            return
         }
 
         // Add the item to the trait info.
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 2507d747452..219c2abff2c 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -57,6 +57,7 @@ use syntax::ext::base::MultiItemModifier;
 use syntax::ext::hygiene::Mark;
 use syntax::ast::{self, FloatTy};
 use syntax::ast::{CRATE_NODE_ID, Name, NodeId, IntTy, UintTy};
+use syntax::ext::base::SyntaxExtension;
 use syntax::parse::token::{self, keywords};
 use syntax::util::lev_distance::find_best_match_for_name;
 
@@ -77,7 +78,7 @@ use std::mem::replace;
 use std::rc::Rc;
 
 use resolve_imports::{ImportDirective, NameResolution};
-use macros::InvocationData;
+use macros::{InvocationData, LegacyBinding};
 
 // NB: This module needs to be declared first so diagnostics are
 // registered before they are used.
@@ -792,9 +793,6 @@ pub struct ModuleS<'a> {
     // access the children must be preceded with a
     // `populate_module_if_necessary` call.
     populated: Cell<bool>,
-
-    macros: RefCell<FnvHashMap<Name, macros::NameBinding>>,
-    macros_escape: bool,
 }
 
 pub type Module<'a> = &'a ModuleS<'a>;
@@ -812,8 +810,6 @@ impl<'a> ModuleS<'a> {
             globs: RefCell::new((Vec::new())),
             traits: RefCell::new(None),
             populated: Cell::new(true),
-            macros: RefCell::new(FnvHashMap()),
-            macros_escape: false,
         }
     }
 
@@ -1087,6 +1083,7 @@ pub struct Resolver<'a> {
     pub derive_modes: FnvHashMap<Name, Rc<MultiItemModifier>>,
     crate_loader: &'a mut CrateLoader,
     macro_names: FnvHashSet<Name>,
+    builtin_macros: FnvHashMap<Name, Rc<SyntaxExtension>>,
 
     // Maps the `Mark` of an expansion to its containing module or block.
     invocations: FnvHashMap<Mark, &'a InvocationData<'a>>,
@@ -1099,6 +1096,7 @@ pub struct ResolverArenas<'a> {
     import_directives: arena::TypedArena<ImportDirective<'a>>,
     name_resolutions: arena::TypedArena<RefCell<NameResolution<'a>>>,
     invocation_data: arena::TypedArena<InvocationData<'a>>,
+    legacy_bindings: arena::TypedArena<LegacyBinding<'a>>,
 }
 
 impl<'a> ResolverArenas<'a> {
@@ -1126,6 +1124,9 @@ impl<'a> ResolverArenas<'a> {
                              -> &'a InvocationData<'a> {
         self.invocation_data.alloc(expansion_data)
     }
+    fn alloc_legacy_binding(&'a self, binding: LegacyBinding<'a>) -> &'a LegacyBinding<'a> {
+        self.legacy_bindings.alloc(binding)
+    }
 }
 
 impl<'a> ty::NodeIdTree for Resolver<'a> {
@@ -1273,6 +1274,7 @@ impl<'a> Resolver<'a> {
             derive_modes: FnvHashMap(),
             crate_loader: crate_loader,
             macro_names: FnvHashSet(),
+            builtin_macros: FnvHashMap(),
             invocations: invocations,
         }
     }
@@ -1285,6 +1287,7 @@ impl<'a> Resolver<'a> {
             import_directives: arena::TypedArena::new(),
             name_resolutions: arena::TypedArena::new(),
             invocation_data: arena::TypedArena::new(),
+            legacy_bindings: arena::TypedArena::new(),
         }
     }
 
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 650642cee70..31b0180f6f1 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -12,6 +12,7 @@ use {Module, Resolver};
 use build_reduced_graph::BuildReducedGraphVisitor;
 use rustc::hir::def_id::{CRATE_DEF_INDEX, DefIndex};
 use rustc::hir::map::{self, DefCollector};
+use rustc::util::nodemap::FnvHashMap;
 use std::cell::Cell;
 use std::rc::Rc;
 use syntax::ast;
@@ -19,41 +20,58 @@ use syntax::errors::DiagnosticBuilder;
 use syntax::ext::base::{self, MultiModifier, MultiDecorator, MultiItemModifier};
 use syntax::ext::base::{NormalTT, SyntaxExtension};
 use syntax::ext::expand::{Expansion, Invocation, InvocationKind};
-use syntax::ext::hygiene::{Mark, SyntaxContext};
+use syntax::ext::hygiene::Mark;
 use syntax::ext::tt::macro_rules;
 use syntax::parse::token::intern;
 use syntax::util::lev_distance::find_best_match_for_name;
-use syntax_pos::{Span, DUMMY_SP};
-
-// FIXME(jseyfried) Merge with `::NameBinding`.
-pub struct NameBinding {
-    pub ext: Rc<SyntaxExtension>,
-    pub expansion: Mark,
-    pub shadowing: bool,
-    pub span: Span,
-}
+use syntax_pos::Span;
 
 #[derive(Clone)]
 pub struct InvocationData<'a> {
-    backtrace: SyntaxContext,
     pub module: Cell<Module<'a>>,
     def_index: DefIndex,
     // True if this expansion is in a `const_integer` position, for example `[u32; m!()]`.
     // c.f. `DefCollector::visit_ast_const_integer`.
     const_integer: bool,
+    // The scope in which the invocation path is resolved.
+    pub legacy_scope: Cell<LegacyScope<'a>>,
+    // The smallest scope that includes this invocation's expansion,
+    // or `Empty` if this invocation has not been expanded yet.
+    pub expansion: Cell<LegacyScope<'a>>,
 }
 
 impl<'a> InvocationData<'a> {
     pub fn root(graph_root: Module<'a>) -> Self {
         InvocationData {
-            backtrace: SyntaxContext::empty(),
             module: Cell::new(graph_root),
             def_index: CRATE_DEF_INDEX,
             const_integer: false,
+            legacy_scope: Cell::new(LegacyScope::Empty),
+            expansion: Cell::new(LegacyScope::Empty),
         }
     }
 }
 
+#[derive(Copy, Clone)]
+pub enum LegacyScope<'a> {
+    Empty,
+    Invocation(&'a InvocationData<'a>), // The scope of the invocation, not including its expansion
+    Expansion(&'a InvocationData<'a>), // The scope of the invocation, including its expansion
+    Binding(&'a LegacyBinding<'a>),
+}
+
+pub struct LegacyBinding<'a> {
+    parent: LegacyScope<'a>,
+    kind: LegacyBindingKind,
+}
+
+pub enum LegacyBindingKind {
+    MacroRules(ast::Name, Rc<SyntaxExtension>, Span),
+    MacroUse(LegacyImports),
+}
+
+pub type LegacyImports = FnvHashMap<ast::Name, (Rc<SyntaxExtension>, Span)>;
+
 impl<'a> base::Resolver for Resolver<'a> {
     fn next_node_id(&mut self) -> ast::NodeId {
         self.session.next_node_id()
@@ -63,18 +81,36 @@ impl<'a> base::Resolver for Resolver<'a> {
         let mark = Mark::fresh();
         let module = self.module_map[&id];
         self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData {
-            backtrace: SyntaxContext::empty(),
             module: Cell::new(module),
             def_index: module.def_id().unwrap().index,
             const_integer: false,
+            legacy_scope: Cell::new(LegacyScope::Empty),
+            expansion: Cell::new(LegacyScope::Empty),
         }));
         mark
     }
 
     fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) {
-        self.collect_def_ids(mark, expansion);
-        self.current_module = self.invocations[&mark].module.get();
-        expansion.visit_with(&mut BuildReducedGraphVisitor { resolver: self, expansion: mark });
+        let invocation = self.invocations[&mark];
+        self.collect_def_ids(invocation, expansion);
+
+        self.current_module = invocation.module.get();
+        let mut visitor = BuildReducedGraphVisitor {
+            resolver: self,
+            legacy_scope: LegacyScope::Invocation(invocation),
+            legacy_imports: FnvHashMap(),
+        };
+        expansion.visit_with(&mut visitor);
+        invocation.expansion.set(visitor.legacy_scope);
+
+        if !visitor.legacy_imports.is_empty() {
+            invocation.legacy_scope.set({
+                LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding {
+                    parent: invocation.legacy_scope.get(),
+                    kind: LegacyBindingKind::MacroUse(visitor.legacy_imports),
+                }))
+            });
+        }
     }
 
     fn add_macro(&mut self, scope: Mark, mut def: ast::MacroDef) {
@@ -83,17 +119,12 @@ impl<'a> base::Resolver for Resolver<'a> {
         }
         if def.use_locally {
             let invocation = self.invocations[&scope];
-            let mut module = invocation.module.get();
-            while module.macros_escape {
-                module = module.parent.unwrap();
-            }
-            let binding = NameBinding {
-                ext: Rc::new(macro_rules::compile(&self.session.parse_sess, &def)),
-                expansion: invocation.backtrace.data().prev_ctxt.data().outer_mark,
-                shadowing: self.resolve_macro_name(scope, def.ident.name, false).is_some(),
-                span: def.span,
-            };
-            module.macros.borrow_mut().insert(def.ident.name, binding);
+            let ext = Rc::new(macro_rules::compile(&self.session.parse_sess, &def));
+            let binding = self.arenas.alloc_legacy_binding(LegacyBinding {
+                parent: invocation.legacy_scope.get(),
+                kind: LegacyBindingKind::MacroRules(def.ident.name, ext, def.span),
+            });
+            invocation.legacy_scope.set(LegacyScope::Binding(binding));
             self.macro_names.insert(def.ident.name);
         }
         if def.export {
@@ -106,12 +137,7 @@ impl<'a> base::Resolver for Resolver<'a> {
         if let NormalTT(..) = *ext {
             self.macro_names.insert(ident.name);
         }
-        self.graph_root.macros.borrow_mut().insert(ident.name, NameBinding {
-            ext: ext,
-            expansion: Mark::root(),
-            shadowing: false,
-            span: DUMMY_SP,
-        });
+        self.builtin_macros.insert(ident.name, ext);
     }
 
     fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>) {
@@ -121,8 +147,8 @@ impl<'a> base::Resolver for Resolver<'a> {
     fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
         for i in 0..attrs.len() {
             let name = intern(&attrs[i].name());
-            match self.invocations[&Mark::root()].module.get().macros.borrow().get(&name) {
-                Some(binding) => match *binding.ext {
+            match self.builtin_macros.get(&name) {
+                Some(ext) => match **ext {
                     MultiModifier(..) | MultiDecorator(..) | SyntaxExtension::AttrProcMacro(..) => {
                         return Some(attrs.remove(i))
                     }
@@ -149,6 +175,7 @@ impl<'a> base::Resolver for Resolver<'a> {
             InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span),
         };
 
+        let scope = self.invocations[&scope].legacy_scope.get();
         self.resolve_macro_name(scope, name, true).or_else(|| {
             let mut err =
                 self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name));
@@ -164,37 +191,63 @@ impl<'a> base::Resolver for Resolver<'a> {
 }
 
 impl<'a> Resolver<'a> {
-    pub fn resolve_macro_name(&mut self, scope: Mark, name: ast::Name, record_used: bool)
-                              -> Option<Rc<SyntaxExtension>> {
-        let invocation = self.invocations[&scope];
-        let mut module = invocation.module.get();
+    fn resolve_macro_name(&mut self,
+                          mut scope: LegacyScope<'a>,
+                          name: ast::Name,
+                          record_used: bool)
+                          -> Option<Rc<SyntaxExtension>> {
+        let check_shadowing = |this: &mut Self, relative_depth, scope, span| {
+            if record_used && relative_depth > 0 &&
+               this.resolve_macro_name(scope, name, false).is_some() &&
+               this.macro_shadowing_errors.insert(span) {
+                let msg = format!("`{}` is already in scope", name);
+                this.session.struct_span_err(span, &msg)
+                    .note("macro-expanded `macro_rules!`s and `#[macro_use]`s \
+                           may not shadow existing macros (see RFC 1560)")
+                    .emit();
+            }
+        };
+
+        let mut relative_depth: u32 = 0;
         loop {
-            if let Some(binding) = module.macros.borrow().get(&name) {
-                let mut backtrace = invocation.backtrace.data();
-                while binding.expansion != backtrace.outer_mark {
-                    if backtrace.outer_mark != Mark::root() {
-                        backtrace = backtrace.prev_ctxt.data();
-                        continue
+            scope = match scope {
+                LegacyScope::Empty => break,
+                LegacyScope::Expansion(invocation) => {
+                    if let LegacyScope::Empty = invocation.expansion.get() {
+                        invocation.legacy_scope.get()
+                    } else {
+                        relative_depth += 1;
+                        invocation.expansion.get()
                     }
-
-                    if record_used && binding.shadowing &&
-                       self.macro_shadowing_errors.insert(binding.span) {
-                        let msg = format!("`{}` is already in scope", name);
-                        self.session.struct_span_err(binding.span, &msg)
-                            .note("macro-expanded `macro_rules!`s and `#[macro_use]`s \
-                                   may not shadow existing macros (see RFC 1560)")
-                            .emit();
+                }
+                LegacyScope::Invocation(invocation) => {
+                    let new_relative_depth = relative_depth.saturating_sub(1);
+                    let mut scope = invocation.legacy_scope.get();
+                    if let LegacyScope::Binding(binding) = scope {
+                        match binding.kind {
+                            LegacyBindingKind::MacroUse(ref imports) => {
+                                if let Some(&(ref ext, span)) = imports.get(&name) {
+                                    check_shadowing(self, relative_depth, binding.parent, span);
+                                    return Some(ext.clone());
+                                }
+                            },
+                            LegacyBindingKind::MacroRules(name_, ref ext, span) => {
+                                if name_ == name {
+                                    check_shadowing(self, new_relative_depth, binding.parent, span);
+                                    return Some(ext.clone());
+                                }
+                            }
+                        }
+                        scope = binding.parent
                     }
-                    break
+                    relative_depth = new_relative_depth;
+                    scope
                 }
-                return Some(binding.ext.clone());
-            }
-            match module.parent {
-                Some(parent) => module = parent,
-                None => break,
-            }
+                _ => unreachable!(),
+            };
         }
-        None
+
+        self.builtin_macros.get(&name).cloned()
     }
 
     fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) {
@@ -207,17 +260,18 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    fn collect_def_ids(&mut self, mark: Mark, expansion: &Expansion) {
+    fn collect_def_ids(&mut self, invocation: &'a InvocationData<'a>, expansion: &Expansion) {
         let Resolver { ref mut invocations, arenas, graph_root, .. } = *self;
-        let InvocationData { def_index, const_integer, backtrace, .. } = invocations[&mark].clone();
+        let InvocationData { def_index, const_integer, .. } = *invocation;
 
         let visit_macro_invoc = &mut |invoc: map::MacroInvocationData| {
             invocations.entry(invoc.mark).or_insert_with(|| {
                 arenas.alloc_invocation_data(InvocationData {
-                    backtrace: backtrace.apply_mark(invoc.mark),
                     def_index: invoc.def_index,
                     const_integer: invoc.const_integer,
                     module: Cell::new(graph_root),
+                    expansion: Cell::new(LegacyScope::Empty),
+                    legacy_scope: Cell::new(LegacyScope::Empty),
                 })
             });
         };