diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index b0a717e18f98d..c0f38061a0d6d 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -18,31 +18,39 @@ use middle::cstore::InlinedItem; use syntax::ast::*; use syntax::visit; -use syntax::parse::token; +use syntax::parse::token::{self, keywords}; /// Creates def ids for nodes in the HIR. -pub struct DefCollector<'ast> { +pub struct DefCollector<'a> { // If we are walking HIR (c.f., AST), we need to keep a reference to the // crate. - hir_crate: Option<&'ast hir::Crate>, - definitions: &'ast mut Definitions, + hir_crate: Option<&'a hir::Crate>, + definitions: &'a mut Definitions, parent_def: Option, + pub visit_macro_invoc: Option<&'a mut FnMut(MacroInvocationData)>, } -impl<'ast> DefCollector<'ast> { - pub fn new(definitions: &'ast mut Definitions) -> DefCollector<'ast> { +pub struct MacroInvocationData { + pub id: NodeId, + pub def_index: DefIndex, + pub const_integer: bool, +} + +impl<'a> DefCollector<'a> { + pub fn new(definitions: &'a mut Definitions) -> Self { DefCollector { hir_crate: None, definitions: definitions, parent_def: None, + visit_macro_invoc: None, } } pub fn extend(parent_node: NodeId, parent_def_path: DefPath, parent_def_id: DefId, - definitions: &'ast mut Definitions) - -> DefCollector<'ast> { + definitions: &'a mut Definitions) + -> Self { let mut collector = DefCollector::new(definitions); assert_eq!(parent_def_path.krate, parent_def_id.krate); @@ -65,7 +73,7 @@ impl<'ast> DefCollector<'ast> { self.create_def_with_parent(Some(CRATE_DEF_INDEX), DUMMY_NODE_ID, DefPathData::Misc); } - pub fn walk_item(&mut self, ii: &'ast InlinedItem, krate: &'ast hir::Crate) { + pub fn walk_item(&mut self, ii: &'a InlinedItem, krate: &'a hir::Crate) { self.hir_crate = Some(krate); ii.visit(self); } @@ -84,29 +92,28 @@ impl<'ast> DefCollector<'ast> { self.definitions.create_def_with_parent(parent, node_id, data) } - fn with_parent(&mut self, parent_def: DefIndex, f: F) { + pub fn with_parent(&mut self, parent_def: DefIndex, f: F) { let parent = self.parent_def; self.parent_def = Some(parent_def); f(self); self.parent_def = parent; } - fn visit_ast_const_integer(&mut self, expr: &Expr) { - // Find the node which will be used after lowering. - if let ExprKind::Paren(ref inner) = expr.node { - return self.visit_ast_const_integer(inner); - } - - // FIXME(eddyb) Closures should have separate - // function definition IDs and expression IDs. - if let ExprKind::Closure(..) = expr.node { - return; + pub fn visit_ast_const_integer(&mut self, expr: &Expr) { + match expr.node { + // Find the node which will be used after lowering. + ExprKind::Paren(ref inner) => return self.visit_ast_const_integer(inner), + ExprKind::Mac(..) => return self.visit_macro_invoc(expr.id, true), + // FIXME(eddyb) Closures should have separate + // function definition IDs and expression IDs. + ExprKind::Closure(..) => return, + _ => {} } self.create_def(expr.id, DefPathData::Initializer); } - fn visit_hir_const_integer(&mut self, expr: &'ast hir::Expr) { + fn visit_hir_const_integer(&mut self, expr: &hir::Expr) { // FIXME(eddyb) Closures should have separate // function definition IDs and expression IDs. if let hir::ExprClosure(..) = expr.node { @@ -115,9 +122,19 @@ impl<'ast> DefCollector<'ast> { self.create_def(expr.id, DefPathData::Initializer); } + + fn visit_macro_invoc(&mut self, id: NodeId, const_integer: bool) { + if let Some(ref mut visit) = self.visit_macro_invoc { + visit(MacroInvocationData { + id: id, + const_integer: const_integer, + def_index: self.parent_def.unwrap(), + }) + } + } } -impl<'ast> visit::Visitor for DefCollector<'ast> { +impl<'a> visit::Visitor for DefCollector<'a> { fn visit_item(&mut self, i: &Item) { debug!("visit_item: {:?}", i); @@ -129,10 +146,14 @@ impl<'ast> visit::Visitor for DefCollector<'ast> { ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) | ItemKind::Trait(..) | ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) | ItemKind::Ty(..) => DefPathData::TypeNs(i.ident.name.as_str()), + ItemKind::Mod(..) if i.ident == keywords::Invalid.ident() => { + return visit::walk_item(self, i); + } ItemKind::Mod(..) => DefPathData::Module(i.ident.name.as_str()), ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) => DefPathData::ValueNs(i.ident.name.as_str()), - ItemKind::Mac(..) => DefPathData::MacroDef(i.ident.name.as_str()), + ItemKind::Mac(..) if i.id == DUMMY_NODE_ID => return, // Scope placeholder + ItemKind::Mac(..) => return self.visit_macro_invoc(i.id, false), ItemKind::Use(..) => DefPathData::Misc, }; let def = self.create_def(i.id, def_data); @@ -198,7 +219,7 @@ impl<'ast> visit::Visitor for DefCollector<'ast> { TraitItemKind::Method(..) | TraitItemKind::Const(..) => DefPathData::ValueNs(ti.ident.name.as_str()), TraitItemKind::Type(..) => DefPathData::TypeNs(ti.ident.name.as_str()), - TraitItemKind::Macro(..) => DefPathData::MacroDef(ti.ident.name.as_str()), + TraitItemKind::Macro(..) => return self.visit_macro_invoc(ti.id, false), }; let def = self.create_def(ti.id, def_data); @@ -216,7 +237,7 @@ impl<'ast> visit::Visitor for DefCollector<'ast> { ImplItemKind::Method(..) | ImplItemKind::Const(..) => DefPathData::ValueNs(ii.ident.name.as_str()), ImplItemKind::Type(..) => DefPathData::TypeNs(ii.ident.name.as_str()), - ImplItemKind::Macro(..) => DefPathData::MacroDef(ii.ident.name.as_str()), + ImplItemKind::Macro(..) => return self.visit_macro_invoc(ii.id, false), }; let def = self.create_def(ii.id, def_data); @@ -232,9 +253,13 @@ impl<'ast> visit::Visitor for DefCollector<'ast> { fn visit_pat(&mut self, pat: &Pat) { let parent_def = self.parent_def; - if let PatKind::Ident(_, id, _) = pat.node { - let def = self.create_def(pat.id, DefPathData::Binding(id.node.name.as_str())); - self.parent_def = Some(def); + match pat.node { + PatKind::Mac(..) => return self.visit_macro_invoc(pat.id, false), + PatKind::Ident(_, id, _) => { + let def = self.create_def(pat.id, DefPathData::Binding(id.node.name.as_str())); + self.parent_def = Some(def); + } + _ => {} } visit::walk_pat(self, pat); @@ -244,13 +269,14 @@ impl<'ast> visit::Visitor for DefCollector<'ast> { fn visit_expr(&mut self, expr: &Expr) { let parent_def = self.parent_def; - if let ExprKind::Repeat(_, ref count) = expr.node { - self.visit_ast_const_integer(count); - } - - if let ExprKind::Closure(..) = expr.node { - let def = self.create_def(expr.id, DefPathData::ClosureExpr); - self.parent_def = Some(def); + match expr.node { + ExprKind::Mac(..) => return self.visit_macro_invoc(expr.id, false), + ExprKind::Repeat(_, ref count) => self.visit_ast_const_integer(count), + ExprKind::Closure(..) => { + let def = self.create_def(expr.id, DefPathData::ClosureExpr); + self.parent_def = Some(def); + } + _ => {} } visit::walk_expr(self, expr); @@ -258,11 +284,13 @@ impl<'ast> visit::Visitor for DefCollector<'ast> { } fn visit_ty(&mut self, ty: &Ty) { - if let TyKind::FixedLengthVec(_, ref length) = ty.node { - self.visit_ast_const_integer(length); - } - if let TyKind::ImplTrait(..) = ty.node { - self.create_def(ty.id, DefPathData::ImplTrait); + match ty.node { + TyKind::Mac(..) => return self.visit_macro_invoc(ty.id, false), + TyKind::FixedLengthVec(_, ref length) => self.visit_ast_const_integer(length), + TyKind::ImplTrait(..) => { + self.create_def(ty.id, DefPathData::ImplTrait); + } + _ => {} } visit::walk_ty(self, ty); } @@ -274,6 +302,13 @@ impl<'ast> visit::Visitor for DefCollector<'ast> { fn visit_macro_def(&mut self, macro_def: &MacroDef) { self.create_def(macro_def.id, DefPathData::MacroDef(macro_def.ident.name.as_str())); } + + fn visit_stmt(&mut self, stmt: &Stmt) { + match stmt.node { + StmtKind::Mac(..) => self.visit_macro_invoc(stmt.id, false), + _ => visit::walk_stmt(self, stmt), + } + } } // We walk the HIR rather than the AST when reading items from metadata. diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs index 5cfb71f4fc877..f87844652cc19 100644 --- a/src/librustc/hir/map/definitions.rs +++ b/src/librustc/hir/map/definitions.rs @@ -9,11 +9,10 @@ // except according to those terms. use hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE}; -use hir::map::def_collector::DefCollector; use rustc_data_structures::fnv::FnvHashMap; use std::fmt::Write; use std::hash::{Hash, Hasher, SipHasher}; -use syntax::{ast, visit}; +use syntax::ast; use syntax::parse::token::{self, InternedString}; use ty::TyCtxt; use util::nodemap::NodeMap; @@ -224,12 +223,6 @@ impl Definitions { } } - pub fn collect(&mut self, krate: &ast::Crate) { - let mut def_collector = DefCollector::new(self); - def_collector.collect_root(); - visit::walk_crate(&mut def_collector, krate); - } - /// Get the number of definitions. pub fn len(&self) -> usize { self.data.len() diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index b351bd427acbc..bafb00edc190c 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -11,7 +11,7 @@ pub use self::Node::*; use self::MapEntry::*; use self::collector::NodeCollector; -use self::def_collector::DefCollector; +pub use self::def_collector::{DefCollector, MacroInvocationData}; pub use self::definitions::{Definitions, DefKey, DefPath, DefPathData, DisambiguatedDefPathData, InlinedRootPath}; diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 9d5dce7ad058e..f9ac1312d3c75 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -639,6 +639,12 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, } sess.track_errors(|| sess.lint_store.borrow_mut().process_command_line(sess))?; + // Currently, we ignore the name resolution data structures for the purposes of dependency + // tracking. Instead we will run name resolution and include its output in the hash of each + // item, much like we do for macro expansion. In other words, the hash reflects not just + // its contents but the results of name resolution on those contents. Hopefully we'll push + // this back at some point. + let _ignore = sess.dep_graph.in_ignore(); let mut crate_loader = CrateLoader::new(sess, &cstore, &krate, crate_name); let resolver_arenas = Resolver::arenas(); let mut resolver = @@ -733,9 +739,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, }) })?; - // Collect defintions for def ids. - time(sess.time_passes(), "collecting defs", || resolver.definitions.collect(&krate)); - time(sess.time_passes(), "early lint checks", || lint::check_ast_crate(sess, &krate)); @@ -745,13 +748,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, || ast_validation::check_crate(sess, &krate)); time(sess.time_passes(), "name resolution", || -> CompileResult { - // Currently, we ignore the name resolution data structures for the purposes of dependency - // tracking. Instead we will run name resolution and include its output in the hash of each - // item, much like we do for macro expansion. In other words, the hash reflects not just - // its contents but the results of name resolution on those contents. Hopefully we'll push - // this back at some point. - let _ignore = sess.dep_graph.in_ignore(); - resolver.build_reduced_graph(&krate); resolver.resolve_imports(); // Since import resolution will eventually happen in expansion, diff --git a/src/librustc_metadata/diagnostics.rs b/src/librustc_metadata/diagnostics.rs index f52e1437acc95..c03375bf8254f 100644 --- a/src/librustc_metadata/diagnostics.rs +++ b/src/librustc_metadata/diagnostics.rs @@ -98,10 +98,10 @@ Erroneous code examples: ```compile_fail,E0466 #[macro_use(a_macro(another_macro))] // error: invalid import declaration -extern crate some_crate; +extern crate core as some_crate; #[macro_use(i_want = "some_macros")] // error: invalid import declaration -extern crate another_crate; +extern crate core as another_crate; ``` This is a syntax error at the level of attribute declarations. The proper @@ -135,10 +135,10 @@ Erroneous code examples: ```compile_fail,E0467 #[macro_reexport] // error: no macros listed for export -extern crate macros_for_good; +extern crate core as macros_for_good; #[macro_reexport(fun_macro = "foo")] // error: not a macro identifier -extern crate other_macros_for_good; +extern crate core as other_macros_for_good; ``` This is a syntax error at the level of attribute declarations. @@ -165,8 +165,8 @@ Example of erroneous code: ```compile_fail,E0468 mod foo { #[macro_use(helpful_macro)] // error: must be at crate root to import - extern crate some_crate; // macros from another crate - helpful_macro!(...) + extern crate core; // macros from another crate + helpful_macro!(...); } ``` diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 9202f8c0946f4..55bf5dcf1cff0 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -20,21 +20,25 @@ use {NameBinding, NameBindingKind, ToNameBinding}; use Resolver; use {resolve_error, resolve_struct_error, ResolutionError}; +use rustc::middle::cstore::LoadedMacro; use rustc::hir::def::*; use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; use rustc::hir::map::DefPathData; use rustc::ty; use std::cell::Cell; +use std::rc::Rc; use syntax::ast::Name; use syntax::attr; use syntax::parse::token; -use syntax::ast::{Block, Crate}; -use syntax::ast::{ForeignItem, ForeignItemKind, Item, ItemKind}; +use syntax::ast::{self, Block, ForeignItem, ForeignItemKind, Item, ItemKind}; use syntax::ast::{Mutability, StmtKind, TraitItem, TraitItemKind}; use syntax::ast::{Variant, ViewPathGlob, ViewPathList, ViewPathSimple}; +use syntax::ext::base::{MultiItemModifier, Resolver as SyntaxResolver}; +use syntax::ext::hygiene::Mark; +use syntax::feature_gate::{self, emit_feature_err}; use syntax::parse::token::keywords; use syntax::visit::{self, Visitor}; @@ -53,11 +57,6 @@ impl<'a> ToNameBinding<'a> for (Def, Span, ty::Visibility) { } impl<'b> Resolver<'b> { - /// Constructs the reduced graph for the entire crate. - pub fn build_reduced_graph(&mut self, krate: &Crate) { - visit::walk_crate(&mut BuildReducedGraphVisitor { resolver: self }, krate); - } - /// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined; /// otherwise, reports an error. fn define(&mut self, parent: Module<'b>, name: Name, ns: Namespace, def: T) @@ -72,15 +71,13 @@ impl<'b> Resolver<'b> { fn block_needs_anonymous_module(&mut self, block: &Block) -> bool { // If any statements are items, we need to create an anonymous module block.stmts.iter().any(|statement| match statement.node { - StmtKind::Item(_) => true, + StmtKind::Item(_) | StmtKind::Mac(_) => true, _ => false, }) } /// Constructs the reduced graph for one item. fn build_reduced_graph_for_item(&mut self, item: &Item) { - self.crate_loader.process_item(item, &self.definitions); - let parent = self.current_module; let name = item.ident.name; let sp = item.span; @@ -188,8 +185,20 @@ impl<'b> Resolver<'b> { } ItemKind::ExternCrate(_) => { - // n.b. we don't need to look at the path option here, because cstore already - // did + // We need to error on `#[macro_use] extern crate` when it isn't at the + // crate root, because `$crate` won't work properly. + let is_crate_root = self.current_module.parent.is_none(); + for def in self.crate_loader.load_macros(item, is_crate_root) { + match def { + LoadedMacro::Def(def) => self.add_macro(Mark::root(), def), + LoadedMacro::CustomDerive(name, ext) => { + self.insert_custom_derive(&name, ext, item.span); + } + } + } + self.crate_loader.process_item(item, &self.definitions); + + // n.b. we don't need to look at the path option here, because cstore already did if let Some(crate_id) = self.session.cstore.extern_mod_stmt_cnum(item.id) { let def_id = DefId { krate: crate_id, @@ -206,6 +215,8 @@ impl<'b> Resolver<'b> { } } + ItemKind::Mod(..) if item.ident == keywords::Invalid.ident() => {} // Crate root + ItemKind::Mod(..) => { let def = Def::Mod(self.definitions.local_def_id(item.id)); let module = self.arenas.alloc_module(ModuleS { @@ -213,6 +224,7 @@ 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)); @@ -222,7 +234,7 @@ impl<'b> Resolver<'b> { self.current_module = module; } - ItemKind::ForeignMod(..) => {} + ItemKind::ForeignMod(..) => self.crate_loader.process_item(item, &self.definitions), // These items live in the value namespace. ItemKind::Static(_, m, _) => { @@ -476,14 +488,79 @@ impl<'b> Resolver<'b> { } module.populated.set(true) } + + // does this attribute list contain "macro_use"? + fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { + for attr in attrs { + if attr.check_name("macro_escape") { + let msg = "macro_escape is a deprecated synonym for macro_use"; + let mut err = self.session.struct_span_warn(attr.span, msg); + if let ast::AttrStyle::Inner = attr.node.style { + err.help("consider an outer attribute, #[macro_use] mod ...").emit(); + } else { + err.emit(); + } + } else if !attr.check_name("macro_use") { + continue; + } + + if !attr.is_word() { + self.session.span_err(attr.span, "arguments to macro_use are not allowed here"); + } + return true; + } + + false + } + + fn insert_custom_derive(&mut self, name: &str, ext: Rc, sp: Span) { + if !self.session.features.borrow().rustc_macro { + let sess = &self.session.parse_sess; + let msg = "loading custom derive macro crates is experimentally supported"; + emit_feature_err(sess, "rustc_macro", sp, feature_gate::GateIssue::Language, msg); + } + if self.derive_modes.insert(token::intern(name), ext).is_some() { + self.session.span_err(sp, &format!("cannot shadow existing derive mode `{}`", name)); + } + } +} + +pub struct BuildReducedGraphVisitor<'a, 'b: 'a> { + pub resolver: &'a mut Resolver<'b>, +} + +impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { + fn visit_invoc(&mut self, id: ast::NodeId) { + self.resolver.expansion_data.get_mut(&id.as_u32()).unwrap().module = + self.resolver.current_module; + } } -struct BuildReducedGraphVisitor<'a, 'b: 'a> { - resolver: &'a mut Resolver<'b>, +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), + } + } + } } 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 { + ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => return, // Scope placeholder + ItemKind::Mac(..) => return self.visit_invoc(item.id), + _ => {} + } + let parent = self.resolver.current_module; self.resolver.build_reduced_graph_for_item(item); visit::walk_item(self, item); @@ -492,6 +569,7 @@ impl<'a, 'b> Visitor for BuildReducedGraphVisitor<'a, 'b> { fn visit_foreign_item(&mut self, foreign_item: &ForeignItem) { self.resolver.build_reduced_graph_for_foreign_item(foreign_item); + visit::walk_foreign_item(self, foreign_item); } fn visit_block(&mut self, block: &Block) { @@ -515,7 +593,7 @@ impl<'a, 'b> Visitor for BuildReducedGraphVisitor<'a, 'b> { (Def::Method(item_def_id), ValueNS) } TraitItemKind::Type(..) => (Def::AssociatedTy(item_def_id), TypeNS), - TraitItemKind::Macro(_) => panic!("unexpanded macro in resolve!"), + TraitItemKind::Macro(_) => return self.visit_invoc(item.id), }; self.resolver.trait_item_map.insert((item.ident.name, def_id), is_static_method); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index fb9819b72ab3e..4e949e3259c45 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -42,7 +42,7 @@ use self::RibKind::*; use self::UseLexicalScopeFlag::*; use self::ModulePrefixResult::*; -use rustc::hir::map::Definitions; +use rustc::hir::map::{Definitions, DefCollector}; use rustc::hir::{self, PrimTy, TyBool, TyChar, TyFloat, TyInt, TyUint, TyStr}; use rustc::middle::cstore::CrateLoader; use rustc::session::Session; @@ -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; @@ -72,9 +73,9 @@ use syntax_pos::{Span, DUMMY_SP}; use errors::DiagnosticBuilder; use std::cell::{Cell, RefCell}; -use std::rc::Rc; use std::fmt; use std::mem::replace; +use std::rc::Rc; use resolve_imports::{ImportDirective, NameResolution}; @@ -786,6 +787,9 @@ pub struct ModuleS<'a> { // access the children must be preceded with a // `populate_module_if_necessary` call. populated: Cell, + + macros: RefCell>>, + macros_escape: bool, } pub type Module<'a> = &'a ModuleS<'a>; @@ -803,6 +807,8 @@ impl<'a> ModuleS<'a> { globs: RefCell::new((Vec::new())), traits: RefCell::new(None), populated: Cell::new(true), + macros: RefCell::new(FnvHashMap()), + macros_escape: false, } } @@ -1074,7 +1080,7 @@ pub struct Resolver<'a> { macro_names: FnvHashSet, // Maps the `Mark` of an expansion to its containing module or block. - expansion_data: FnvHashMap, + expansion_data: FnvHashMap>, } pub struct ResolverArenas<'a> { @@ -1188,13 +1194,16 @@ impl<'a> Resolver<'a> { let mut module_map = NodeMap(); module_map.insert(CRATE_NODE_ID, graph_root); + let mut definitions = Definitions::new(); + DefCollector::new(&mut definitions).collect_root(); + let mut expansion_data = FnvHashMap(); - expansion_data.insert(0, macros::ExpansionData::default()); // Crate root expansion + expansion_data.insert(0, macros::ExpansionData::root(graph_root)); // Crate root expansion Resolver { session: session, - definitions: Definitions::new(), + definitions: definitions, macros_at_scope: FnvHashMap(), // The outermost module has def ID 0; this is not reflected in the @@ -1264,6 +1273,13 @@ impl<'a> Resolver<'a> { /// Entry point to crate resolution. pub fn resolve_crate(&mut self, krate: &Crate) { + // Collect `DefId`s for exported macro defs. + for def in &krate.exported_macros { + DefCollector::new(&mut self.definitions).with_parent(CRATE_DEF_INDEX, |collector| { + collector.visit_macro_def(def) + }) + } + self.current_module = self.graph_root; visit::walk_crate(self, krate); diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 3a9fb845190d2..17f2dff28c3f4 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -8,36 +8,38 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use Resolver; -use rustc::middle::cstore::LoadedMacro; -use rustc::util::nodemap::FnvHashMap; -use std::cell::RefCell; -use std::mem; +use {Module, Resolver}; +use build_reduced_graph::BuildReducedGraphVisitor; +use rustc::hir::def_id::{CRATE_DEF_INDEX, DefIndex}; +use rustc::hir::map::{self, DefCollector}; use std::rc::Rc; -use syntax::ast::{self, Name}; +use syntax::ast; use syntax::errors::DiagnosticBuilder; use syntax::ext::base::{self, MultiModifier, MultiDecorator, MultiItemModifier}; -use syntax::ext::base::{NormalTT, Resolver as SyntaxResolver, SyntaxExtension}; +use syntax::ext::base::{NormalTT, SyntaxExtension}; use syntax::ext::expand::{Expansion, Invocation, InvocationKind}; use syntax::ext::hygiene::Mark; use syntax::ext::tt::macro_rules; -use syntax::feature_gate::{self, emit_feature_err}; -use syntax::parse::token::{self, intern}; +use syntax::parse::token::intern; use syntax::util::lev_distance::find_best_match_for_name; -use syntax::visit::{self, Visitor}; -use syntax_pos::Span; -#[derive(Clone, Default)] -pub struct ExpansionData { - module: Rc, +#[derive(Clone)] +pub struct ExpansionData<'a> { + pub module: 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, } -// FIXME(jseyfried): merge with `::ModuleS`. -#[derive(Default)] -struct ModuleData { - parent: Option>, - macros: RefCell>>, - macros_escape: bool, +impl<'a> ExpansionData<'a> { + pub fn root(graph_root: Module<'a>) -> Self { + ExpansionData { + module: graph_root, + def_index: CRATE_DEF_INDEX, + const_integer: false, + } + } } impl<'a> base::Resolver for Resolver<'a> { @@ -45,11 +47,21 @@ impl<'a> base::Resolver for Resolver<'a> { self.session.next_node_id() } - fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) { - expansion.visit_with(&mut ExpansionVisitor { - current_module: self.expansion_data[&mark.as_u32()].module.clone(), - resolver: self, + fn get_module_scope(&mut self, id: ast::NodeId) -> Mark { + let mark = Mark::fresh(); + let module = self.module_map[&id]; + self.expansion_data.insert(mark.as_u32(), ExpansionData { + module: module, + def_index: module.def_id().unwrap().index, + const_integer: false, }); + mark + } + + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) { + self.collect_def_ids(mark, expansion); + self.current_module = self.expansion_data[&mark.as_u32()].module; + expansion.visit_with(&mut BuildReducedGraphVisitor { resolver: self }); } fn add_macro(&mut self, scope: Mark, mut def: ast::MacroDef) { @@ -71,9 +83,9 @@ impl<'a> base::Resolver for Resolver<'a> { self.macro_names.insert(ident.name); } - let mut module = self.expansion_data[&scope.as_u32()].module.clone(); + let mut module = self.expansion_data[&scope.as_u32()].module; while module.macros_escape { - module = module.parent.clone().unwrap(); + module = module.parent.unwrap(); } module.macros.borrow_mut().insert(ident.name, ext); } @@ -113,12 +125,12 @@ impl<'a> base::Resolver for Resolver<'a> { InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span), }; - let mut module = self.expansion_data[&scope.as_u32()].module.clone(); + let mut module = self.expansion_data[&scope.as_u32()].module; loop { if let Some(ext) = module.macros.borrow().get(&name) { return Some(ext.clone()); } - match module.parent.clone() { + match module.parent { Some(parent) => module = parent, None => break, } @@ -147,116 +159,23 @@ impl<'a> Resolver<'a> { } } - fn insert_custom_derive(&mut self, name: &str, ext: Rc, sp: Span) { - if !self.session.features.borrow().rustc_macro { - let sess = &self.session.parse_sess; - let msg = "loading custom derive macro crates is experimentally supported"; - emit_feature_err(sess, "rustc_macro", sp, feature_gate::GateIssue::Language, msg); - } - if self.derive_modes.insert(token::intern(name), ext).is_some() { - self.session.span_err(sp, &format!("cannot shadow existing derive mode `{}`", name)); - } - } -} - -struct ExpansionVisitor<'b, 'a: 'b> { - resolver: &'b mut Resolver<'a>, - current_module: Rc, -} + fn collect_def_ids(&mut self, mark: Mark, expansion: &Expansion) { + let expansion_data = &mut self.expansion_data; + let ExpansionData { def_index, const_integer, module } = expansion_data[&mark.as_u32()]; + let visit_macro_invoc = &mut |invoc: map::MacroInvocationData| { + expansion_data.entry(invoc.id.as_u32()).or_insert(ExpansionData { + def_index: invoc.def_index, + const_integer: invoc.const_integer, + module: module, + }); + }; -impl<'a, 'b> ExpansionVisitor<'a, 'b> { - fn visit_invoc(&mut self, id: ast::NodeId) { - self.resolver.expansion_data.insert(id.as_u32(), ExpansionData { - module: self.current_module.clone(), + let mut def_collector = DefCollector::new(&mut self.definitions); + def_collector.visit_macro_invoc = Some(visit_macro_invoc); + def_collector.with_parent(def_index, |def_collector| if !const_integer { + expansion.visit_with(def_collector) + } else if let Expansion::Expr(ref expr) = *expansion { + def_collector.visit_ast_const_integer(expr); }); } - - // does this attribute list contain "macro_use"? - fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { - for attr in attrs { - if attr.check_name("macro_escape") { - let msg = "macro_escape is a deprecated synonym for macro_use"; - let mut err = self.resolver.session.struct_span_warn(attr.span, msg); - if let ast::AttrStyle::Inner = attr.node.style { - err.help("consider an outer attribute, #[macro_use] mod ...").emit(); - } else { - err.emit(); - } - } else if !attr.check_name("macro_use") { - continue; - } - - if !attr.is_word() { - self.resolver.session.span_err(attr.span, - "arguments to macro_use are not allowed here"); - } - return true; - } - - false - } -} - -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), - } - } - } -} - -impl<'a, 'b> Visitor for ExpansionVisitor<'a, 'b> { - method!(visit_trait_item: ast::TraitItem, ast::TraitItemKind::Macro, walk_trait_item); - 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: &ast::Item) { - match item.node { - ast::ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => {} // Scope placeholder - ast::ItemKind::Mac(..) => self.visit_invoc(item.id), - ast::ItemKind::Mod(..) => { - let module_data = ModuleData { - parent: Some(self.current_module.clone()), - macros: RefCell::new(FnvHashMap()), - macros_escape: self.contains_macro_use(&item.attrs), - }; - let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data)); - visit::walk_item(self, item); - self.current_module = orig_module; - } - ast::ItemKind::ExternCrate(..) => { - // We need to error on `#[macro_use] extern crate` when it isn't at the - // crate root, because `$crate` won't work properly. - // FIXME(jseyfried): This will be nicer once `ModuleData` is merged with `ModuleS`. - let is_crate_root = self.current_module.parent.as_ref().unwrap().parent.is_none(); - for def in self.resolver.crate_loader.load_macros(item, is_crate_root) { - match def { - LoadedMacro::Def(def) => self.resolver.add_macro(Mark::root(), def), - LoadedMacro::CustomDerive(name, ext) => { - self.resolver.insert_custom_derive(&name, ext, item.span); - } - } - } - visit::walk_item(self, item); - } - _ => visit::walk_item(self, item), - } - } - - fn visit_block(&mut self, block: &ast::Block) { - let module_data = ModuleData { - parent: Some(self.current_module.clone()), - macros: RefCell::new(FnvHashMap()), - macros_escape: false, - }; - let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data)); - visit::walk_block(self, block); - self.current_module = orig_module; - } } diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 78d047c7651f9..94a7f6030b937 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -303,6 +303,6 @@ fn is_cfg(attr: &ast::Attribute) -> bool { attr.check_name("cfg") } -fn is_test_or_bench(attr: &ast::Attribute) -> bool { +pub fn is_test_or_bench(attr: &ast::Attribute) -> bool { attr.check_name("test") || attr.check_name("bench") } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 3e85565beb6d1..1d2a0dd0cbc65 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -656,6 +656,7 @@ pub type NamedSyntaxExtension = (Name, SyntaxExtension); pub trait Resolver { fn next_node_id(&mut self) -> ast::NodeId; + fn get_module_scope(&mut self, id: ast::NodeId) -> Mark; fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion); fn add_macro(&mut self, scope: Mark, def: ast::MacroDef); @@ -671,6 +672,7 @@ pub struct DummyResolver; impl Resolver for DummyResolver { fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID } + fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() } fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {} fn add_macro(&mut self, _scope: Mark, _def: ast::MacroDef) {} @@ -708,6 +710,7 @@ pub struct ExtCtxt<'a> { pub ecfg: expand::ExpansionConfig<'a>, pub crate_root: Option<&'static str>, pub resolver: &'a mut Resolver, + pub resolve_err_count: usize, pub current_expansion: ExpansionData, } @@ -722,6 +725,7 @@ impl<'a> ExtCtxt<'a> { ecfg: ecfg, crate_root: None, resolver: resolver, + resolve_err_count: 0, current_expansion: ExpansionData { mark: Mark::root(), depth: 0, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 43c622189632a..aad895750c869 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -16,7 +16,7 @@ use ext::placeholders::{placeholder, PlaceholderExpander}; use attr::{self, HasAttrs}; use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use syntax_pos::{self, Span, ExpnId}; -use config::StripUnconfigured; +use config::{is_test_or_bench, StripUnconfigured}; use ext::base::*; use feature_gate::{self, Features}; use fold; @@ -200,7 +200,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { _ => unreachable!(), }; - if self.cx.parse_sess.span_diagnostic.err_count() > err_count { + if self.cx.parse_sess.span_diagnostic.err_count() - self.cx.resolve_err_count > err_count { self.cx.parse_sess.span_diagnostic.abort_if_errors(); } @@ -271,8 +271,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.cfg = crate_config; if self.monotonic { + let err_count = self.cx.parse_sess.span_diagnostic.err_count(); let mark = self.cx.current_expansion.mark; self.cx.resolver.visit_expansion(mark, &result.0); + self.cx.resolve_err_count += self.cx.parse_sess.span_diagnostic.err_count() - err_count; } result @@ -610,7 +612,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_item(&mut self, item: P) -> SmallVector> { let item = configure!(self, item); - let (item, attr) = self.classify_item(item); + let (mut item, attr) = self.classify_item(item); if let Some(attr) = attr { let item = Annotatable::Item(fully_configure!(self, item, noop_fold_item)); return self.collect_attr(attr, item, ExpansionKind::Items).make_items(); @@ -667,6 +669,13 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { self.cx.current_expansion.module = orig_module; return result; } + // Ensure that test functions are accessible from the test harness. + ast::ItemKind::Fn(..) if self.cx.ecfg.should_test => { + if item.attrs.iter().any(|attr| is_test_or_bench(attr)) { + item = item.map(|mut item| { item.vis = ast::Visibility::Public; item }); + } + noop_fold_item(item, self) + } _ => noop_fold_item(item, self), } } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 6327e8f71bcd5..e4510520a55a4 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -119,7 +119,7 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> { } debug!("current path: {}", path_name_i(&self.cx.path)); - let i = if is_test_fn(&self.cx, &i) || is_bench_fn(&self.cx, &i) { + if is_test_fn(&self.cx, &i) || is_bench_fn(&self.cx, &i) { match i.node { ast::ItemKind::Fn(_, ast::Unsafety::Unsafe, _, _, _, _) => { let diag = self.cx.span_diagnostic; @@ -136,54 +136,37 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> { }; self.cx.testfns.push(test); self.tests.push(i.ident); - // debug!("have {} test/bench functions", - // cx.testfns.len()); - - // Make all tests public so we can call them from outside - // the module (note that the tests are re-exported and must - // be made public themselves to avoid privacy errors). - i.map(|mut i| { - i.vis = ast::Visibility::Public; - i - }) } } - } else { - i - }; + } + let mut item = i.unwrap(); // We don't want to recurse into anything other than mods, since // mods or tests inside of functions will break things - let res = match i.node { - ast::ItemKind::Mod(..) => fold::noop_fold_item(i, self), - _ => SmallVector::one(i), - }; + if let ast::ItemKind::Mod(module) = item.node { + let tests = mem::replace(&mut self.tests, Vec::new()); + let tested_submods = mem::replace(&mut self.tested_submods, Vec::new()); + let mut mod_folded = fold::noop_fold_mod(module, self); + let tests = mem::replace(&mut self.tests, tests); + let tested_submods = mem::replace(&mut self.tested_submods, tested_submods); + + if !tests.is_empty() || !tested_submods.is_empty() { + let (it, sym) = mk_reexport_mod(&mut self.cx, item.id, tests, tested_submods); + mod_folded.items.push(it); + + if !self.cx.path.is_empty() { + self.tested_submods.push((self.cx.path[self.cx.path.len()-1], sym)); + } else { + debug!("pushing nothing, sym: {:?}", sym); + self.cx.toplevel_reexport = Some(sym); + } + } + item.node = ast::ItemKind::Mod(mod_folded); + } if ident.name != keywords::Invalid.name() { self.cx.path.pop(); } - res - } - - fn fold_mod(&mut self, m: ast::Mod) -> ast::Mod { - let tests = mem::replace(&mut self.tests, Vec::new()); - let tested_submods = mem::replace(&mut self.tested_submods, Vec::new()); - let mut mod_folded = fold::noop_fold_mod(m, self); - let tests = mem::replace(&mut self.tests, tests); - let tested_submods = mem::replace(&mut self.tested_submods, tested_submods); - - if !tests.is_empty() || !tested_submods.is_empty() { - let (it, sym) = mk_reexport_mod(&mut self.cx, tests, tested_submods); - mod_folded.items.push(it); - - if !self.cx.path.is_empty() { - self.tested_submods.push((self.cx.path[self.cx.path.len()-1], sym)); - } else { - debug!("pushing nothing, sym: {:?}", sym); - self.cx.toplevel_reexport = Some(sym); - } - } - - mod_folded + SmallVector::one(P(item)) } fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { mac } @@ -239,7 +222,7 @@ impl fold::Folder for EntryPointCleaner { fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { mac } } -fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec, +fn mk_reexport_mod(cx: &mut TestCtxt, parent: ast::NodeId, tests: Vec, tested_submods: Vec<(ast::Ident, ast::Ident)>) -> (P, ast::Ident) { let super_ = token::str_to_ident("super"); @@ -257,6 +240,8 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec, }; let sym = token::gensym_ident("__test_reexports"); + let parent = if parent == ast::DUMMY_NODE_ID { ast::CRATE_NODE_ID } else { parent }; + cx.ext_cx.current_expansion.mark = cx.ext_cx.resolver.get_module_scope(parent); let it = cx.ext_cx.monotonic_expander().fold_item(P(ast::Item { ident: sym.clone(), attrs: Vec::new(), diff --git a/src/test/codegen-units/item-collection/overloaded-operators.rs b/src/test/codegen-units/item-collection/overloaded-operators.rs index c275eb954b094..0295311334b6b 100644 --- a/src/test/codegen-units/item-collection/overloaded-operators.rs +++ b/src/test/codegen-units/item-collection/overloaded-operators.rs @@ -45,8 +45,8 @@ impl IndexMut for Indexable { } -//~ TRANS_ITEM fn overloaded_operators::{{impl}}[2]::eq[0] -//~ TRANS_ITEM fn overloaded_operators::{{impl}}[2]::ne[0] +//~ TRANS_ITEM fn overloaded_operators::{{impl}}[4]::eq[0] +//~ TRANS_ITEM fn overloaded_operators::{{impl}}[4]::ne[0] #[derive(PartialEq)] pub struct Equatable(u32); @@ -54,7 +54,7 @@ pub struct Equatable(u32); impl Add for Equatable { type Output = u32; - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[3]::add[0] + //~ TRANS_ITEM fn overloaded_operators::{{impl}}[2]::add[0] fn add(self, rhs: u32) -> u32 { self.0 + rhs } @@ -63,7 +63,7 @@ impl Add for Equatable { impl Deref for Equatable { type Target = u32; - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[4]::deref[0] + //~ TRANS_ITEM fn overloaded_operators::{{impl}}[3]::deref[0] fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/test/compile-fail-fulldeps/derive-no-std-not-supported.rs b/src/test/compile-fail-fulldeps/derive-no-std-not-supported.rs index 01c81a8bbcee4..6ae5544d68699 100644 --- a/src/test/compile-fail-fulldeps/derive-no-std-not-supported.rs +++ b/src/test/compile-fail-fulldeps/derive-no-std-not-supported.rs @@ -10,7 +10,6 @@ #![no_std] -extern crate core; extern crate rand; extern crate serialize as rustc_serialize; diff --git a/src/test/compile-fail/gated-non-ascii-idents.rs b/src/test/compile-fail/gated-non-ascii-idents.rs index f4b9830d579a4..9e042c3a7d50e 100644 --- a/src/test/compile-fail/gated-non-ascii-idents.rs +++ b/src/test/compile-fail/gated-non-ascii-idents.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate bäz; //~ ERROR non-ascii idents +extern crate core as bäz; //~ ERROR non-ascii idents use föö::bar; //~ ERROR non-ascii idents diff --git a/src/test/compile-fail/self_type_keyword.rs b/src/test/compile-fail/self_type_keyword.rs index b9c9d7a389b95..1622378a71c7c 100644 --- a/src/test/compile-fail/self_type_keyword.rs +++ b/src/test/compile-fail/self_type_keyword.rs @@ -39,11 +39,17 @@ pub fn main() { } } -use std::option::Option as Self; -//~^ ERROR expected identifier, found keyword `Self` +mod m1 { + extern crate core as Self; + //~^ ERROR expected identifier, found keyword `Self` +} -extern crate Self; -//~^ ERROR expected identifier, found keyword `Self` +mod m2 { + use std::option::Option as Self; + //~^ ERROR expected identifier, found keyword `Self` +} -trait Self {} -//~^ ERROR expected identifier, found keyword `Self` +mod m3 { + trait Self {} + //~^ ERROR expected identifier, found keyword `Self` +}