diff --git a/src/doc/unstable-book/src/language-features/decl-macro.md b/src/doc/unstable-book/src/language-features/decl-macro.md new file mode 100644 index 0000000000000..4700b252e2d1a --- /dev/null +++ b/src/doc/unstable-book/src/language-features/decl-macro.md @@ -0,0 +1,10 @@ +# `decl_macro` + +The tracking issue for this feature is: [#39412] + +[#39412]: /~https://github.com/rust-lang/rust/issues/39412 + +------------------------ + + + diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index 771031db0c045..feded417ce17f 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs @@ -117,7 +117,7 @@ pub type ExportMap = NodeMap>; #[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] pub struct Export { - pub name: ast::Name, // The name of the target. + pub ident: ast::Ident, // The name of the target. pub def: Def, // The definition of the target. pub span: Span, // The span of the target definition. } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index d359c69d3a092..91cfbc38aa0ec 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -47,7 +47,7 @@ use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX}; use hir::def::{Def, PathResolution}; use rustc_data_structures::indexed_vec::IndexVec; use session::Session; -use util::nodemap::{DefIdMap, NodeMap}; +use util::nodemap::{DefIdMap, FxHashMap, NodeMap}; use std::collections::BTreeMap; use std::fmt::Debug; @@ -77,6 +77,7 @@ pub struct LoweringContext<'a> { // a definition, then we can properly create the def id. parent_def: Option, resolver: &'a mut Resolver, + name_map: FxHashMap, /// The items being lowered are collected here. items: BTreeMap, @@ -126,6 +127,7 @@ pub fn lower_crate(sess: &Session, sess: sess, parent_def: None, resolver: resolver, + name_map: FxHashMap(), items: BTreeMap::new(), trait_items: BTreeMap::new(), impl_items: BTreeMap::new(), @@ -393,7 +395,7 @@ impl<'a> LoweringContext<'a> { } fn allow_internal_unstable(&self, reason: &'static str, mut span: Span) -> Span { - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); mark.set_expn_info(codemap::ExpnInfo { call_site: span, callee: codemap::NameAndSpan { @@ -495,6 +497,14 @@ impl<'a> LoweringContext<'a> { } } + fn lower_ident(&mut self, ident: Ident) -> Name { + let ident = ident.modern(); + if ident.ctxt == SyntaxContext::empty() { + return ident.name; + } + *self.name_map.entry(ident).or_insert_with(|| Symbol::from_ident(ident)) + } + fn lower_opt_sp_ident(&mut self, o_id: Option>) -> Option> { o_id.map(|sp_ident| respan(sp_ident.span, sp_ident.node.name)) } @@ -546,7 +556,7 @@ impl<'a> LoweringContext<'a> { fn lower_ty_binding(&mut self, b: &TypeBinding) -> hir::TypeBinding { hir::TypeBinding { id: self.lower_node_id(b.id), - name: b.ident.name, + name: self.lower_ident(b.ident), ty: self.lower_ty(&b.ty), span: b.span, } @@ -844,7 +854,7 @@ impl<'a> LoweringContext<'a> { } hir::PathSegment { - name: segment.identifier.name, + name: self.lower_ident(segment.identifier), parameters: parameters, } } @@ -941,7 +951,7 @@ impl<'a> LoweringContext<'a> { } fn lower_ty_param(&mut self, tp: &TyParam, add_bounds: &[TyParamBound]) -> hir::TyParam { - let mut name = tp.ident.name; + let mut name = self.lower_ident(tp.ident); // Don't expose `Self` (recovered "keyword used as ident" parse error). // `rustc::ty` expects `Self` to be only used for a trait's `Self`. @@ -975,7 +985,7 @@ impl<'a> LoweringContext<'a> { fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime { hir::Lifetime { id: self.lower_node_id(l.id), - name: l.name, + name: self.lower_ident(l.ident), span: l.span, } } @@ -1137,7 +1147,11 @@ impl<'a> LoweringContext<'a> { hir::StructField { span: f.span, id: self.lower_node_id(f.id), - name: f.ident.map(|ident| ident.name).unwrap_or(Symbol::intern(&index.to_string())), + name: self.lower_ident(match f.ident { + Some(ident) => ident, + // FIXME(jseyfried) positional field hygiene + None => Ident { name: Symbol::intern(&index.to_string()), ctxt: f.span.ctxt }, + }), vis: self.lower_visibility(&f.vis, None), ty: self.lower_ty(&f.ty), attrs: self.lower_attrs(&f.attrs), @@ -1146,7 +1160,7 @@ impl<'a> LoweringContext<'a> { fn lower_field(&mut self, f: &Field) -> hir::Field { hir::Field { - name: respan(f.ident.span, f.ident.node.name), + name: respan(f.ident.span, self.lower_ident(f.ident.node)), expr: P(self.lower_expr(&f.expr)), span: f.span, is_shorthand: f.is_shorthand, @@ -1371,7 +1385,7 @@ impl<'a> LoweringContext<'a> { self.with_parent_def(i.id, |this| { hir::TraitItem { id: this.lower_node_id(i.id), - name: i.ident.name, + name: this.lower_ident(i.ident), attrs: this.lower_attrs(&i.attrs), node: match i.node { TraitItemKind::Const(ref ty, ref default) => { @@ -1421,7 +1435,7 @@ impl<'a> LoweringContext<'a> { }; hir::TraitItemRef { id: hir::TraitItemId { node_id: i.id }, - name: i.ident.name, + name: self.lower_ident(i.ident), span: i.span, defaultness: self.lower_defaultness(Defaultness::Default, has_default), kind: kind, @@ -1432,7 +1446,7 @@ impl<'a> LoweringContext<'a> { self.with_parent_def(i.id, |this| { hir::ImplItem { id: this.lower_node_id(i.id), - name: i.ident.name, + name: this.lower_ident(i.ident), attrs: this.lower_attrs(&i.attrs), vis: this.lower_visibility(&i.vis, None), defaultness: this.lower_defaultness(i.defaultness, true /* [1] */), @@ -1461,7 +1475,7 @@ impl<'a> LoweringContext<'a> { fn lower_impl_item_ref(&mut self, i: &ImplItem) -> hir::ImplItemRef { hir::ImplItemRef { id: hir::ImplItemId { node_id: i.id }, - name: i.ident.name, + name: self.lower_ident(i.ident), span: i.span, vis: self.lower_visibility(&i.vis, Some(i.id)), defaultness: self.lower_defaultness(i.defaultness, true /* [1] */), @@ -1501,17 +1515,23 @@ impl<'a> LoweringContext<'a> { pub fn lower_item(&mut self, i: &Item) -> Option { let mut name = i.ident.name; + let mut vis = self.lower_visibility(&i.vis, None); let attrs = self.lower_attrs(&i.attrs); - if let ItemKind::MacroDef(ref tts) = i.node { - if i.attrs.iter().any(|attr| attr.path == "macro_export") { + if let ItemKind::MacroDef(ref def) = i.node { + if !def.legacy || i.attrs.iter().any(|attr| attr.path == "macro_export") { self.exported_macros.push(hir::MacroDef { - name: name, attrs: attrs, id: i.id, span: i.span, body: tts.clone().into(), + name: name, + vis: vis, + attrs: attrs, + id: i.id, + span: i.span, + body: def.stream(), + legacy: def.legacy, }); } return None; } - let mut vis = self.lower_visibility(&i.vis, None); let node = self.with_parent_def(i.id, |this| { this.lower_item_kind(i.id, &mut name, &attrs, &mut vis, &i.node) }); @@ -1654,7 +1674,7 @@ impl<'a> LoweringContext<'a> { Spanned { span: f.span, node: hir::FieldPat { - name: f.node.ident.name, + name: self.lower_ident(f.node.ident), pat: self.lower_pat(&f.node.pat), is_shorthand: f.node.is_shorthand, }, @@ -1824,7 +1844,7 @@ impl<'a> LoweringContext<'a> { ExprKind::MethodCall(i, ref tps, ref args) => { let tps = tps.iter().map(|x| self.lower_ty(x)).collect(); let args = args.iter().map(|x| self.lower_expr(x)).collect(); - hir::ExprMethodCall(respan(i.span, i.node.name), tps, args) + hir::ExprMethodCall(respan(i.span, self.lower_ident(i.node)), tps, args) } ExprKind::Binary(binop, ref lhs, ref rhs) => { let binop = self.lower_binop(binop); @@ -1923,7 +1943,8 @@ impl<'a> LoweringContext<'a> { P(self.lower_expr(er))) } ExprKind::Field(ref el, ident) => { - hir::ExprField(P(self.lower_expr(el)), respan(ident.span, ident.node.name)) + hir::ExprField(P(self.lower_expr(el)), + respan(ident.span, self.lower_ident(ident.node))) } ExprKind::TupField(ref el, ident) => { hir::ExprTupField(P(self.lower_expr(el)), ident) @@ -2641,11 +2662,9 @@ impl<'a> LoweringContext<'a> { let parent_def = self.parent_def.unwrap(); let def_id = { let defs = self.resolver.definitions(); - let def_path_data = DefPathData::Binding(name.as_str()); - let def_index = defs.create_def_with_parent(parent_def, - id, - def_path_data, - REGULAR_SPACE); + let def_path_data = DefPathData::Binding(Ident::with_empty_ctxt(name)); + let def_index = defs + .create_def_with_parent(parent_def, id, def_path_data, REGULAR_SPACE, Mark::root()); DefId::local(def_index) }; diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index 7ff5152c71a2c..cb25b462b6e24 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -14,7 +14,7 @@ use hir::def_id::{CRATE_DEF_INDEX, DefIndex, DefIndexAddressSpace}; use syntax::ast::*; use syntax::ext::hygiene::Mark; use syntax::visit; -use syntax::symbol::{Symbol, keywords}; +use syntax::symbol::keywords; use hir::map::{ITEM_LIKE_SPACE, REGULAR_SPACE}; @@ -22,6 +22,7 @@ use hir::map::{ITEM_LIKE_SPACE, REGULAR_SPACE}; pub struct DefCollector<'a> { definitions: &'a mut Definitions, parent_def: Option, + expansion: Mark, pub visit_macro_invoc: Option<&'a mut FnMut(MacroInvocationData)>, } @@ -32,9 +33,10 @@ pub struct MacroInvocationData { } impl<'a> DefCollector<'a> { - pub fn new(definitions: &'a mut Definitions) -> Self { + pub fn new(definitions: &'a mut Definitions, expansion: Mark) -> Self { DefCollector { definitions: definitions, + expansion: expansion, parent_def: None, visit_macro_invoc: None, } @@ -54,7 +56,8 @@ impl<'a> DefCollector<'a> { -> DefIndex { let parent_def = self.parent_def.unwrap(); debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def); - self.definitions.create_def_with_parent(parent_def, node_id, data, address_space) + self.definitions + .create_def_with_parent(parent_def, node_id, data, address_space, self.expansion) } pub fn with_parent(&mut self, parent_def: DefIndex, f: F) { @@ -100,14 +103,14 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { DefPathData::Impl, ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) | ItemKind::Trait(..) | ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) | ItemKind::Ty(..) => - DefPathData::TypeNs(i.ident.name.as_str()), + DefPathData::TypeNs(i.ident.modern()), ItemKind::Mod(..) if i.ident == keywords::Invalid.ident() => { return visit::walk_item(self, i); } - ItemKind::Mod(..) => DefPathData::Module(i.ident.name.as_str()), + ItemKind::Mod(..) => DefPathData::Module(i.ident.modern()), ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) => - DefPathData::ValueNs(i.ident.name.as_str()), - ItemKind::MacroDef(..) => DefPathData::MacroDef(i.ident.name.as_str()), + DefPathData::ValueNs(i.ident.modern()), + ItemKind::MacroDef(..) => DefPathData::MacroDef(i.ident.modern()), ItemKind::Mac(..) => return self.visit_macro_invoc(i.id, false), ItemKind::GlobalAsm(..) => DefPathData::Misc, ItemKind::Use(ref view_path) => { @@ -135,15 +138,13 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { for v in &enum_definition.variants { let variant_def_index = this.create_def(v.node.data.id(), - DefPathData::EnumVariant(v.node.name.name.as_str()), + DefPathData::EnumVariant(v.node.name.modern()), REGULAR_SPACE); this.with_parent(variant_def_index, |this| { for (index, field) in v.node.data.fields().iter().enumerate() { - let name = field.ident.map(|ident| ident.name) - .unwrap_or_else(|| Symbol::intern(&index.to_string())); - this.create_def(field.id, - DefPathData::Field(name.as_str()), - REGULAR_SPACE); + let ident = field.ident.map(Ident::modern) + .unwrap_or_else(|| Ident::from_str(&index.to_string())); + this.create_def(field.id, DefPathData::Field(ident), REGULAR_SPACE); } if let Some(ref expr) = v.node.disr_expr { @@ -161,9 +162,9 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { } for (index, field) in struct_def.fields().iter().enumerate() { - let name = field.ident.map(|ident| ident.name.as_str()) - .unwrap_or(Symbol::intern(&index.to_string()).as_str()); - this.create_def(field.id, DefPathData::Field(name), REGULAR_SPACE); + let ident = field.ident.map(Ident::modern) + .unwrap_or_else(|| Ident::from_str(&index.to_string())); + this.create_def(field.id, DefPathData::Field(ident), REGULAR_SPACE); } } _ => {} @@ -174,7 +175,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) { let def = self.create_def(foreign_item.id, - DefPathData::ValueNs(foreign_item.ident.name.as_str()), + DefPathData::ValueNs(foreign_item.ident.modern()), REGULAR_SPACE); self.with_parent(def, |this| { @@ -185,7 +186,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { fn visit_generics(&mut self, generics: &'a Generics) { for ty_param in generics.ty_params.iter() { self.create_def(ty_param.id, - DefPathData::TypeParam(ty_param.ident.name.as_str()), + DefPathData::TypeParam(ty_param.ident.modern()), REGULAR_SPACE); } @@ -195,8 +196,8 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { fn visit_trait_item(&mut self, ti: &'a TraitItem) { let def_data = match ti.node { TraitItemKind::Method(..) | TraitItemKind::Const(..) => - DefPathData::ValueNs(ti.ident.name.as_str()), - TraitItemKind::Type(..) => DefPathData::TypeNs(ti.ident.name.as_str()), + DefPathData::ValueNs(ti.ident.modern()), + TraitItemKind::Type(..) => DefPathData::TypeNs(ti.ident.modern()), TraitItemKind::Macro(..) => return self.visit_macro_invoc(ti.id, false), }; @@ -213,8 +214,8 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { fn visit_impl_item(&mut self, ii: &'a ImplItem) { let def_data = match ii.node { ImplItemKind::Method(..) | ImplItemKind::Const(..) => - DefPathData::ValueNs(ii.ident.name.as_str()), - ImplItemKind::Type(..) => DefPathData::TypeNs(ii.ident.name.as_str()), + DefPathData::ValueNs(ii.ident.modern()), + ImplItemKind::Type(..) => DefPathData::TypeNs(ii.ident.modern()), ImplItemKind::Macro(..) => return self.visit_macro_invoc(ii.id, false), }; @@ -235,7 +236,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { 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()), + DefPathData::Binding(id.node.modern()), REGULAR_SPACE); self.parent_def = Some(def); } @@ -280,7 +281,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { fn visit_lifetime_def(&mut self, def: &'a LifetimeDef) { self.create_def(def.lifetime.id, - DefPathData::LifetimeDef(def.lifetime.name.as_str()), + DefPathData::LifetimeDef(def.lifetime.ident.modern()), REGULAR_SPACE); } diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs index 9537b40b28a0b..c86b140fbc614 100644 --- a/src/librustc/hir/map/definitions.rs +++ b/src/librustc/hir/map/definitions.rs @@ -23,7 +23,8 @@ use rustc_data_structures::stable_hasher::StableHasher; use serialize::{Encodable, Decodable, Encoder, Decoder}; use std::fmt::Write; use std::hash::Hash; -use syntax::ast; +use syntax::ast::{self, Ident}; +use syntax::ext::hygiene::{Mark, SyntaxContext}; use syntax::symbol::{Symbol, InternedString}; use ty::TyCtxt; use util::nodemap::NodeMap; @@ -180,6 +181,8 @@ pub struct Definitions { node_to_def_index: NodeMap, def_index_to_node: [Vec; 2], pub(super) node_to_hir_id: IndexVec, + macro_def_scopes: FxHashMap, + expansions: FxHashMap, } // Unfortunately we have to provide a manual impl of Clone because of the @@ -194,6 +197,8 @@ impl Clone for Definitions { self.def_index_to_node[1].clone(), ], node_to_hir_id: self.node_to_hir_id.clone(), + macro_def_scopes: self.macro_def_scopes.clone(), + expansions: self.expansions.clone(), } } } @@ -322,7 +327,7 @@ impl DefPath { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum DefPathData { // Root: these should only be used for the root nodes, because // they are treated specially by the `def_path` function. @@ -336,31 +341,31 @@ pub enum DefPathData { /// An impl Impl, /// Something in the type NS - TypeNs(InternedString), + TypeNs(Ident), /// Something in the value NS - ValueNs(InternedString), + ValueNs(Ident), /// A module declaration - Module(InternedString), + Module(Ident), /// A macro rule - MacroDef(InternedString), + MacroDef(Ident), /// A closure expression ClosureExpr, // Subportions of items /// A type parameter (generic parameter) - TypeParam(InternedString), + TypeParam(Ident), /// A lifetime definition - LifetimeDef(InternedString), + LifetimeDef(Ident), /// A variant of a enum - EnumVariant(InternedString), + EnumVariant(Ident), /// A struct field - Field(InternedString), + Field(Ident), /// Implicit ctor for a tuple-like struct StructCtor, /// Initializer for a const Initializer, /// Pattern binding - Binding(InternedString), + Binding(Ident), /// An `impl Trait` type node. ImplTrait, /// A `typeof` type node. @@ -379,6 +384,8 @@ impl Definitions { node_to_def_index: NodeMap(), def_index_to_node: [vec![], vec![]], node_to_hir_id: IndexVec::new(), + macro_def_scopes: FxHashMap(), + expansions: FxHashMap(), } } @@ -472,7 +479,8 @@ impl Definitions { parent: DefIndex, node_id: ast::NodeId, data: DefPathData, - address_space: DefIndexAddressSpace) + address_space: DefIndexAddressSpace, + expansion: Mark) -> DefIndex { debug!("create_def_with_parent(parent={:?}, node_id={:?}, data={:?})", parent, node_id, data); @@ -510,6 +518,9 @@ impl Definitions { assert_eq!(index.as_array_index(), self.def_index_to_node[address_space.index()].len()); self.def_index_to_node[address_space.index()].push(node_id); + if expansion.is_modern() { + self.expansions.insert(index, expansion); + } debug!("create_def_with_parent: def_index_to_node[{:?} <-> {:?}", index, node_id); self.node_to_def_index.insert(node_id, index); @@ -525,21 +536,33 @@ impl Definitions { "Trying initialize NodeId -> HirId mapping twice"); self.node_to_hir_id = mapping; } + + pub fn expansion(&self, index: DefIndex) -> Mark { + self.expansions.get(&index).cloned().unwrap_or(Mark::root()) + } + + pub fn macro_def_scope(&self, mark: Mark) -> DefId { + self.macro_def_scopes[&mark] + } + + pub fn add_macro_def_scope(&mut self, mark: Mark, scope: DefId) { + self.macro_def_scopes.insert(mark, scope); + } } impl DefPathData { - pub fn get_opt_name(&self) -> Option { + pub fn get_opt_ident(&self) -> Option { use self::DefPathData::*; match *self { - TypeNs(ref name) | - ValueNs(ref name) | - Module(ref name) | - MacroDef(ref name) | - TypeParam(ref name) | - LifetimeDef(ref name) | - EnumVariant(ref name) | - Binding(ref name) | - Field(ref name) => Some(Symbol::intern(name)), + TypeNs(ident) | + ValueNs(ident) | + Module(ident) | + MacroDef(ident) | + TypeParam(ident) | + LifetimeDef(ident) | + EnumVariant(ident) | + Binding(ident) | + Field(ident) => Some(ident), Impl | CrateRoot | @@ -552,19 +575,23 @@ impl DefPathData { } } + pub fn get_opt_name(&self) -> Option { + self.get_opt_ident().map(|ident| ident.name) + } + pub fn as_interned_str(&self) -> InternedString { use self::DefPathData::*; let s = match *self { - TypeNs(ref name) | - ValueNs(ref name) | - Module(ref name) | - MacroDef(ref name) | - TypeParam(ref name) | - LifetimeDef(ref name) | - EnumVariant(ref name) | - Binding(ref name) | - Field(ref name) => { - return name.clone(); + TypeNs(ident) | + ValueNs(ident) | + Module(ident) | + MacroDef(ident) | + TypeParam(ident) | + LifetimeDef(ident) | + EnumVariant(ident) | + Binding(ident) | + Field(ident) => { + return ident.name.as_str(); } // note that this does not show up in user printouts @@ -586,3 +613,25 @@ impl DefPathData { self.as_interned_str().to_string() } } + +impl Eq for DefPathData {} +impl PartialEq for DefPathData { + fn eq(&self, other: &DefPathData) -> bool { + ::std::mem::discriminant(self) == ::std::mem::discriminant(other) && + self.get_opt_ident() == other.get_opt_ident() + } +} + +impl ::std::hash::Hash for DefPathData { + fn hash(&self, hasher: &mut H) { + ::std::mem::discriminant(self).hash(hasher); + if let Some(ident) = self.get_opt_ident() { + if ident.ctxt == SyntaxContext::empty() && ident.name == ident.name.interned() { + ident.name.as_str().hash(hasher) + } else { + // FIXME(jseyfried) implement stable hashing for idents with macros 2.0 hygiene info + ident.hash(hasher) + } + } + } +} diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 868730edfedda..09e9d7c5b2f7d 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -637,14 +637,15 @@ impl<'hir> Map<'hir> { /// Returns the NodeId of `id`'s nearest module parent, or `id` itself if no /// module parent is in this map. - pub fn get_module_parent(&self, id: NodeId) -> NodeId { - match self.walk_parent_nodes(id, |node| match *node { + pub fn get_module_parent(&self, id: NodeId) -> DefId { + let id = match self.walk_parent_nodes(id, |node| match *node { NodeItem(&Item { node: Item_::ItemMod(_), .. }) => true, _ => false, }) { Ok(id) => id, Err(id) => id, - } + }; + self.local_def_id(id) } /// Returns the nearest enclosing scope. A scope is an item or block. diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index cb7f530b9952f..500e95a8a778e 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -162,7 +162,7 @@ impl Lifetime { } pub fn is_static(&self) -> bool { - self.name == keywords::StaticLifetime.name() + self.name == "'static" } } @@ -532,10 +532,12 @@ impl Crate { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct MacroDef { pub name: Name, + pub vis: Visibility, pub attrs: HirVec, pub id: NodeId, pub span: Span, pub body: TokenStream, + pub legacy: bool, } #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index abc51601b6ece..dbe91e2725def 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -329,9 +329,11 @@ impl_stable_hash_for!(enum hir::QPath { impl_stable_hash_for!(struct hir::MacroDef { name, + vis, attrs, id, span, + legacy, body }); @@ -1116,7 +1118,7 @@ impl<'a, 'tcx> HashStable> for hir::def_id::DefIn } impl_stable_hash_for!(struct hir::def::Export { - name, + ident, def, span }); diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 7138db01339f6..0bfe04dd25dca 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -130,7 +130,7 @@ impl_stable_hash_for!(enum ::syntax::ast::FloatTy { F32, F64 }); impl_stable_hash_for!(enum ::syntax::ast::Unsafety { Unsafe, Normal }); impl_stable_hash_for!(enum ::syntax::ast::Constness { Const, NotConst }); impl_stable_hash_for!(enum ::syntax::ast::Defaultness { Default, Final }); -impl_stable_hash_for!(struct ::syntax::ast::Lifetime { id, span, name }); +impl_stable_hash_for!(struct ::syntax::ast::Lifetime { id, span, ident }); impl_stable_hash_for!(enum ::syntax::ast::StrStyle { Cooked, Raw(pounds) }); impl_stable_hash_for!(enum ::syntax::ast::AttrStyle { Outer, Inner }); diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 2b4b5cc73337b..d1d9dd4853d7b 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -28,6 +28,7 @@ #![feature(conservative_impl_trait)] #![feature(const_fn)] #![feature(core_intrinsics)] +#![feature(discriminant_value)] #![feature(i128_type)] #![feature(libc)] #![feature(never_type)] diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index e7c8d3285c8fe..4c25a455f292d 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -50,7 +50,6 @@ use hir; use hir::def_id::LOCAL_CRATE; use hir::intravisit as hir_visit; use syntax::visit as ast_visit; -use syntax::tokenstream::ThinTokenStream; /// Information about the registered lints. /// @@ -1209,7 +1208,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> { run_lints!(self, check_attribute, early_passes, attr); } - fn visit_mac_def(&mut self, _mac: &'a ThinTokenStream, id: ast::NodeId) { + fn visit_mac_def(&mut self, _mac: &'a ast::MacroDef, id: ast::NodeId) { let lints = self.sess.lints.borrow_mut().take(id); for early_lint in lints { self.early_lint(&early_lint); diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 7d7308d73bb04..1a07423bcbc0f 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -26,7 +26,6 @@ use std::mem::replace; use syntax::ast; use syntax::attr; use syntax::ptr::P; -use syntax::symbol::keywords; use syntax_pos::Span; use errors::DiagnosticBuilder; use util::nodemap::{NodeMap, NodeSet, FxHashSet, FxHashMap, DefIdMap}; @@ -746,7 +745,7 @@ fn object_lifetime_defaults_for_item(hir_map: &Map, generics: &hir::Generics) match set { Set1::Empty => Set1::Empty, Set1::One(name) => { - if name == keywords::StaticLifetime.name() { + if name == "'static" { Set1::One(Region::Static) } else { generics.lifetimes.iter().enumerate().find(|&(_, def)| { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index fa731f6dde638..963ee9b7165e5 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -45,8 +45,9 @@ use std::rc::Rc; use std::slice; use std::vec::IntoIter; use std::mem; -use syntax::ast::{self, DUMMY_NODE_ID, Name, NodeId}; +use syntax::ast::{self, DUMMY_NODE_ID, Name, Ident, NodeId}; use syntax::attr; +use syntax::ext::hygiene::{Mark, SyntaxContext}; use syntax::symbol::{Symbol, InternedString}; use syntax_pos::{DUMMY_SP, Span}; use rustc_const_math::ConstInt; @@ -268,7 +269,7 @@ impl Visibility { def => Visibility::Restricted(def.def_id()), }, hir::Inherited => { - Visibility::Restricted(tcx.hir.local_def_id(tcx.hir.get_module_parent(id))) + Visibility::Restricted(tcx.hir.get_module_parent(id)) } } } @@ -1823,17 +1824,22 @@ impl<'a, 'gcx, 'tcx> AdtDef { impl<'a, 'gcx, 'tcx> VariantDef { #[inline] - pub fn find_field_named(&self, - name: ast::Name) - -> Option<&FieldDef> { - self.fields.iter().find(|f| f.name == name) + pub fn find_field_named(&self, name: ast::Name) -> Option<&FieldDef> { + self.index_of_field_named(name).map(|index| &self.fields[index]) } - #[inline] - pub fn index_of_field_named(&self, - name: ast::Name) - -> Option { - self.fields.iter().position(|f| f.name == name) + pub fn index_of_field_named(&self, name: ast::Name) -> Option { + if let Some(index) = self.fields.iter().position(|f| f.name == name) { + return Some(index); + } + let mut ident = name.to_ident(); + while ident.ctxt != SyntaxContext::empty() { + ident.ctxt.remove_mark(); + if let Some(field) = self.fields.iter().position(|f| f.name.to_ident() == ident) { + return Some(field); + } + } + None } #[inline] @@ -2257,10 +2263,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - pub fn vis_is_accessible_from(self, vis: Visibility, block: NodeId) -> bool { - vis.is_accessible_from(self.hir.local_def_id(self.hir.get_module_parent(block)), self) - } - pub fn item_name(self, id: DefId) -> ast::Name { if let Some(id) = self.hir.as_local_node_id(id) { self.hir.name(id) @@ -2372,6 +2374,22 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Err(self.sess.cstore.crate_name(impl_did.krate)) } } + + pub fn adjust(self, name: Name, scope: DefId, block: NodeId) -> (Ident, DefId) { + self.adjust_ident(name.to_ident(), scope, block) + } + + pub fn adjust_ident(self, mut ident: Ident, scope: DefId, block: NodeId) -> (Ident, DefId) { + let expansion = match scope.krate { + LOCAL_CRATE => self.hir.definitions().expansion(scope.index), + _ => Mark::root(), + }; + let scope = match ident.ctxt.adjust(expansion) { + Some(macro_def) => self.hir.definitions().macro_def_scope(macro_def), + None => self.hir.get_module_parent(block), + }; + (ident, scope) + } } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 7ed5f620816e1..b35b086599165 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -154,7 +154,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { } } - let module = self.tcx.hir.local_def_id(self.tcx.hir.get_module_parent(scrut.id)); + let module = self.tcx.hir.get_module_parent(scrut.id); MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| { let mut have_errors = false; @@ -182,7 +182,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { // Then, if the match has no arms, check whether the scrutinee // is uninhabited. let pat_ty = self.tables.node_id_to_type(scrut.id); - let module = self.tcx.hir.local_def_id(self.tcx.hir.get_module_parent(scrut.id)); + let module = self.tcx.hir.get_module_parent(scrut.id); if inlined_arms.is_empty() { let scrutinee_is_uninhabited = if self.tcx.sess.features.borrow().never_type { pat_ty.is_uninhabited_from(module, self.tcx) @@ -231,7 +231,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { "local binding" }; - let module = self.tcx.hir.local_def_id(self.tcx.hir.get_module_parent(pat.id)); + let module = self.tcx.hir.get_module_parent(pat.id); MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| { let mut patcx = PatternContext::new(self.tcx, self.tables); let pattern = patcx.lower_pattern(pat); diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 7478f902e061a..9d098557367cd 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -386,7 +386,10 @@ impl CrateStore for cstore::CStore { id: ast::DUMMY_NODE_ID, span: local_span, attrs: attrs.iter().cloned().collect(), - node: ast::ItemKind::MacroDef(body.into()), + node: ast::ItemKind::MacroDef(ast::MacroDef { + tokens: body.into(), + legacy: def.legacy, + }), vis: ast::Visibility::Inherited, }) } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index d8826d87d4d0f..1db5821f31878 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -39,7 +39,7 @@ use std::u32; use rustc_serialize::{Decodable, Decoder, SpecializedDecoder, opaque}; use syntax::attr; -use syntax::ast; +use syntax::ast::{self, Ident}; use syntax::codemap; use syntax::ext::base::MacroKind; use syntax_pos::{self, Span, BytePos, Pos, DUMMY_SP, NO_EXPANSION}; @@ -667,7 +667,8 @@ impl<'a, 'tcx> CrateMetadata { }, ext.kind() ); - callback(def::Export { name: name, def: def, span: DUMMY_SP }); + let ident = Ident::with_empty_ctxt(name); + callback(def::Export { ident: ident, def: def, span: DUMMY_SP }); } } return @@ -703,7 +704,7 @@ impl<'a, 'tcx> CrateMetadata { if let Some(def) = self.get_def(child_index) { callback(def::Export { def: def, - name: self.item_name(child_index), + ident: Ident::with_empty_ctxt(self.item_name(child_index)), span: self.entry(child_index).span.decode(self), }); } @@ -720,7 +721,8 @@ impl<'a, 'tcx> CrateMetadata { let span = child.span.decode(self); if let (Some(def), Some(name)) = (self.get_def(child_index), def_key.disambiguated_data.data.get_opt_name()) { - callback(def::Export { def: def, name: name, span: span }); + let ident = Ident::with_empty_ctxt(name); + callback(def::Export { def: def, ident: ident, span: span }); // For non-reexport structs and variants add their constructors to children. // Reexport lists automatically contain constructors when necessary. match def { @@ -728,7 +730,7 @@ impl<'a, 'tcx> CrateMetadata { if let Some(ctor_def_id) = self.get_struct_ctor_def_id(child_index) { let ctor_kind = self.get_ctor_kind(child_index); let ctor_def = Def::StructCtor(ctor_def_id, ctor_kind); - callback(def::Export { def: ctor_def, name: name, span: span }); + callback(def::Export { def: ctor_def, ident: ident, span: span }); } } Def::Variant(def_id) => { @@ -736,7 +738,7 @@ impl<'a, 'tcx> CrateMetadata { // value namespace, they are reserved for possible future use. let ctor_kind = self.get_ctor_kind(child_index); let ctor_def = Def::VariantCtor(def_id, ctor_kind); - callback(def::Export { def: ctor_def, name: name, span: span }); + callback(def::Export { def: ctor_def, ident: ident, span: span }); } _ => {} } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 93fcdc455e5dd..07c475949d435 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -1104,6 +1104,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { Entry { kind: EntryKind::MacroDef(self.lazy(&MacroDef { body: pprust::tts_to_string(¯o_def.body.trees().collect::>()), + legacy: macro_def.legacy, })), visibility: self.lazy(&ty::Visibility::Public), span: self.lazy(¯o_def.span), diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 91a22d922199d..2ffe7cc02aaac 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -433,9 +433,10 @@ impl_stable_hash_for!(struct ModData { reexports }); #[derive(RustcEncodable, RustcDecodable)] pub struct MacroDef { pub body: String, + pub legacy: bool, } -impl_stable_hash_for!(struct MacroDef { body }); +impl_stable_hash_for!(struct MacroDef { body, legacy }); #[derive(RustcEncodable, RustcDecodable)] pub struct FnData { diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index d7fee7f3110f4..79d90210d47f7 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -103,11 +103,11 @@ impl<'a> AstValidator<'a> { impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_lifetime(&mut self, lt: &'a Lifetime) { - if lt.name == "'_" { + if lt.ident.name == "'_" { self.session.add_lint(lint::builtin::LIFETIME_UNDERSCORE, lt.id, lt.span, - format!("invalid lifetime name `{}`", lt.name)); + format!("invalid lifetime name `{}`", lt.ident)); } visit::walk_lifetime(self, lt) diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 2ced61f5753a9..153da91db68f8 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -28,7 +28,7 @@ extern crate syntax_pos; use rustc::hir::{self, PatKind}; use rustc::hir::def::Def; -use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, CrateNum, DefId}; +use rustc::hir::def_id::{LOCAL_CRATE, CrateNum, DefId}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::itemlikevisit::DeepVisitor; use rustc::lint; @@ -37,7 +37,8 @@ use rustc::ty::{self, TyCtxt, Ty, TypeFoldable}; use rustc::ty::fold::TypeVisitor; use rustc::ty::maps::Providers; use rustc::util::nodemap::NodeSet; -use syntax::ast; +use syntax::ast::{self, CRATE_NODE_ID, Ident}; +use syntax::symbol::keywords; use syntax_pos::Span; use std::cmp; @@ -344,7 +345,35 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { } fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) { - self.update(md.id, Some(AccessLevel::Public)); + if md.legacy { + self.update(md.id, Some(AccessLevel::Public)); + return + } + + let module_did = ty::DefIdTree::parent(self.tcx, self.tcx.hir.local_def_id(md.id)).unwrap(); + let mut module_id = self.tcx.hir.as_local_node_id(module_did).unwrap(); + let level = if md.vis == hir::Public { self.get(module_id) } else { None }; + let level = self.update(md.id, level); + if level.is_none() { + return + } + + loop { + let module = if module_id == ast::CRATE_NODE_ID { + &self.tcx.hir.krate().module + } else if let hir::ItemMod(ref module) = self.tcx.hir.expect_item(module_id).node { + module + } else { + unreachable!() + }; + for id in &module.item_ids { + self.update(id.id, level); + } + if module_id == ast::CRATE_NODE_ID { + break + } + module_id = self.tcx.hir.get_parent_node(module_id); + } } fn visit_ty(&mut self, ty: &'tcx hir::Ty) { @@ -425,13 +454,15 @@ impl<'b, 'a, 'tcx> TypeVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b struct NamePrivacyVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, tables: &'a ty::TypeckTables<'tcx>, - current_item: DefId, + current_item: ast::NodeId, } impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { // Checks that a field is accessible. fn check_field(&mut self, span: Span, def: &'tcx ty::AdtDef, field: &'tcx ty::FieldDef) { - if !def.is_enum() && !field.vis.is_accessible_from(self.current_item, self.tcx) { + let ident = Ident { ctxt: span.ctxt.modern(), ..keywords::Invalid.ident() }; + let def_id = self.tcx.adjust_ident(ident, def.did, self.current_item).1; + if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) { struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private", field.name, def.variant_descr(), self.tcx.item_path_str(def.did)) .span_label(span, format!("field `{}` is private", field.name)) @@ -455,7 +486,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { } fn visit_item(&mut self, item: &'tcx hir::Item) { - let orig_current_item = replace(&mut self.current_item, self.tcx.hir.local_def_id(item.id)); + let orig_current_item = replace(&mut self.current_item, item.id); intravisit::walk_item(self, item); self.current_item = orig_current_item; } @@ -1182,7 +1213,7 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut visitor = NamePrivacyVisitor { tcx: tcx, tables: &ty::TypeckTables::empty(), - current_item: DefId::local(CRATE_DEF_INDEX), + current_item: CRATE_NODE_ID, }; intravisit::walk_crate(&mut visitor, krate); diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 57639a1ecef7b..597a62f86884b 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -23,7 +23,7 @@ use {resolve_error, resolve_struct_error, ResolutionError}; use rustc::middle::cstore::LoadedMacro; use rustc::hir::def::*; -use rustc::hir::def_id::{CrateNum, BUILTIN_MACROS_CRATE, CRATE_DEF_INDEX, DefId}; +use rustc::hir::def_id::{BUILTIN_MACROS_CRATE, CRATE_DEF_INDEX, LOCAL_CRATE, DefId}; use rustc::ty; use std::cell::Cell; @@ -77,7 +77,7 @@ struct LegacyMacroImports { impl<'a> Resolver<'a> { /// 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<'a>, ident: Ident, ns: Namespace, def: T) + pub fn define(&mut self, parent: Module<'a>, ident: Ident, ns: Namespace, def: T) where T: ToNameBinding<'a>, { let binding = def.to_name_binding(self.arenas); @@ -150,7 +150,7 @@ impl<'a> Resolver<'a> { view_path.span, ResolutionError::SelfImportsOnlyAllowedWithin); } else if source_name == "$crate" && full_path.segments.len() == 1 { - let crate_root = self.resolve_crate_var(source.ctxt, item.span); + let crate_root = self.resolve_crate_root(source.ctxt); let crate_name = match crate_root.kind { ModuleKind::Def(_, name) => name, ModuleKind::Block(..) => unreachable!(), @@ -247,7 +247,8 @@ impl<'a> Resolver<'a> { // n.b. we don't need to look at the path option here, because cstore already did let crate_id = self.session.cstore.extern_mod_stmt_cnum(item.id).unwrap(); - let module = self.get_extern_crate_root(crate_id, item.span); + let module = + self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }); self.populate_module_if_necessary(module); let used = self.process_legacy_macro_imports(item, module, expansion); let binding = @@ -279,7 +280,7 @@ impl<'a> Resolver<'a> { no_implicit_prelude: parent.no_implicit_prelude || { attr::contains_name(&item.attrs, "no_implicit_prelude") }, - ..ModuleData::new(Some(parent), module_kind, def_id, item.span) + ..ModuleData::new(Some(parent), module_kind, def_id, expansion, item.span) }); self.define(parent, ident, TypeNS, (module, vis, sp, expansion)); self.module_map.insert(def_id, module); @@ -317,6 +318,7 @@ impl<'a> Resolver<'a> { let module = self.new_module(parent, module_kind, parent.normal_ancestor_id, + expansion, item.span); self.define(parent, ident, TypeNS, (module, vis, sp, expansion)); @@ -376,6 +378,7 @@ impl<'a> Resolver<'a> { let module = self.new_module(parent, module_kind, parent.normal_ancestor_id, + expansion, item.span); self.define(parent, ident, TypeNS, (module, vis, sp, expansion)); self.current_module = module; @@ -421,12 +424,13 @@ impl<'a> Resolver<'a> { self.define(parent, item.ident, ValueNS, (def, vis, item.span, expansion)); } - fn build_reduced_graph_for_block(&mut self, block: &Block) { + fn build_reduced_graph_for_block(&mut self, block: &Block, expansion: Mark) { let parent = self.current_module; if self.block_needs_anonymous_module(block) { let module = self.new_module(parent, ModuleKind::Block(block.id), parent.normal_ancestor_id, + expansion, block.span); self.block_map.insert(block.id, module); self.current_module = module; // Descend into the block. @@ -435,28 +439,29 @@ impl<'a> Resolver<'a> { /// Builds the reduced graph for a single item in an external crate. fn build_reduced_graph_for_external_crate_def(&mut self, parent: Module<'a>, child: Export) { - let ident = Ident::with_empty_ctxt(child.name); + let ident = child.ident; let def = child.def; let def_id = def.def_id(); let vis = self.session.cstore.visibility(def_id); let span = child.span; - + let expansion = Mark::root(); // FIXME(jseyfried) intercrate hygiene match def { Def::Mod(..) | Def::Enum(..) => { let module = self.new_module(parent, ModuleKind::Def(def, ident.name), def_id, + expansion, span); - self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, expansion)); } Def::Variant(..) | Def::TyAlias(..) => { - self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, expansion)); } Def::Fn(..) | Def::Static(..) | Def::Const(..) | Def::VariantCtor(..) => { - self.define(parent, ident, ValueNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, ValueNS, (def, vis, DUMMY_SP, expansion)); } Def::StructCtor(..) => { - self.define(parent, ident, ValueNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, ValueNS, (def, vis, DUMMY_SP, expansion)); if let Some(struct_def_id) = self.session.cstore.def_key(def_id).parent @@ -469,47 +474,59 @@ impl<'a> Resolver<'a> { let module = self.new_module(parent, module_kind, parent.normal_ancestor_id, + expansion, span); - self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, expansion)); for child in self.session.cstore.item_children(def_id) { let ns = if let Def::AssociatedTy(..) = child.def { TypeNS } else { ValueNS }; - let ident = Ident::with_empty_ctxt(child.name); - self.define(module, ident, ns, (child.def, ty::Visibility::Public, - DUMMY_SP, Mark::root())); + self.define(module, child.ident, ns, + (child.def, ty::Visibility::Public, DUMMY_SP, expansion)); - let has_self = self.session.cstore.associated_item_cloned(child.def.def_id()) - .method_has_self_argument; - self.trait_item_map.insert((def_id, child.name, ns), (child.def, has_self)); + if self.session.cstore.associated_item_cloned(child.def.def_id()) + .method_has_self_argument { + self.has_self.insert(child.def.def_id()); + } } module.populated.set(true); } Def::Struct(..) | Def::Union(..) => { - self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, expansion)); // Record field names for error reporting. let field_names = self.session.cstore.struct_field_names(def_id); self.insert_field_names(def_id, field_names); } Def::Macro(..) => { - self.define(parent, ident, MacroNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, MacroNS, (def, vis, DUMMY_SP, expansion)); } _ => bug!("unexpected definition: {:?}", def) } } - fn get_extern_crate_root(&mut self, cnum: CrateNum, span: Span) -> Module<'a> { - let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; - let name = self.session.cstore.crate_name(cnum); - let macros_only = self.session.cstore.dep_kind(cnum).macros_only(); - let module_kind = ModuleKind::Def(Def::Mod(def_id), name); - let arenas = self.arenas; - *self.extern_crate_roots.entry((cnum, macros_only)).or_insert_with(|| { - arenas.alloc_module(ModuleData::new(None, module_kind, def_id, span)) - }) + pub fn get_module(&mut self, def_id: DefId) -> Module<'a> { + if def_id.krate == LOCAL_CRATE { + return self.module_map[&def_id] + } + + let macros_only = self.session.cstore.dep_kind(def_id.krate).macros_only(); + if let Some(&module) = self.extern_module_map.get(&(def_id, macros_only)) { + return module; + } + + let (name, parent) = if def_id.index == CRATE_DEF_INDEX { + (self.session.cstore.crate_name(def_id.krate), None) + } else { + let def_key = self.session.cstore.def_key(def_id); + (def_key.disambiguated_data.data.get_opt_name().unwrap(), + Some(self.get_module(DefId { index: def_key.parent.unwrap(), ..def_id }))) + }; + + let kind = ModuleKind::Def(Def::Mod(def_id), name); + self.arenas.alloc_module(ModuleData::new(parent, kind, def_id, Mark::root(), DUMMY_SP)) } - pub fn macro_def_scope(&mut self, expansion: Mark, span: Span) -> Module<'a> { + pub fn macro_def_scope(&mut self, expansion: Mark) -> Module<'a> { let def_id = self.macro_defs[&expansion]; if let Some(id) = self.definitions.as_local_node_id(def_id) { self.local_macro_def_scopes[&id] @@ -518,7 +535,7 @@ impl<'a> Resolver<'a> { self.graph_root } else { let module_def_id = ty::DefIdTree::parent(&*self, def_id).unwrap(); - self.get_extern_crate_root(module_def_id.krate, span) + self.get_module(module_def_id) } } @@ -625,7 +642,7 @@ impl<'a> Resolver<'a> { let ident = Ident::with_empty_ctxt(name); let result = self.resolve_ident_in_module(module, ident, MacroNS, false, false, span); if let Ok(binding) = result { - self.macro_exports.push(Export { name: name, def: binding.def(), span: span }); + self.macro_exports.push(Export { ident: ident, def: binding.def(), span: span }); } else { span_err!(self.session, span, E0470, "reexported macro not found"); } @@ -730,7 +747,7 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> { fn visit_item(&mut self, item: &'a Item) { let macro_use = match item.node { ItemKind::MacroDef(..) => { - self.resolver.define_macro(item, &mut self.legacy_scope); + self.resolver.define_macro(item, self.expansion, &mut self.legacy_scope); return } ItemKind::Mac(..) => { @@ -765,7 +782,7 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> { fn visit_block(&mut self, block: &'a Block) { let (parent, legacy_scope) = (self.resolver.current_module, self.legacy_scope); - self.resolver.build_reduced_graph_for_block(block); + self.resolver.build_reduced_graph_for_block(block, self.expansion); visit::walk_block(self, block); self.resolver.current_module = parent; self.legacy_scope = legacy_scope; @@ -773,7 +790,6 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> { fn visit_trait_item(&mut self, item: &'a TraitItem) { let parent = self.resolver.current_module; - let def_id = parent.def_id().unwrap(); if let TraitItemKind::Macro(_) = item.node { self.visit_invoc(item.id); @@ -782,16 +798,18 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> { // Add the item to the trait info. let item_def_id = self.resolver.definitions.local_def_id(item.id); - let (def, ns, has_self) = match item.node { - TraitItemKind::Const(..) => (Def::AssociatedConst(item_def_id), ValueNS, false), - TraitItemKind::Method(ref sig, _) => - (Def::Method(item_def_id), ValueNS, sig.decl.has_self()), - TraitItemKind::Type(..) => (Def::AssociatedTy(item_def_id), TypeNS, false), + let (def, ns) = match item.node { + TraitItemKind::Const(..) => (Def::AssociatedConst(item_def_id), ValueNS), + TraitItemKind::Method(ref sig, _) => { + if sig.decl.has_self() { + self.resolver.has_self.insert(item_def_id); + } + (Def::Method(item_def_id), ValueNS) + } + TraitItemKind::Type(..) => (Def::AssociatedTy(item_def_id), TypeNS), TraitItemKind::Macro(_) => bug!(), // handled above }; - self.resolver.trait_item_map.insert((def_id, item.ident.name, ns), (def, has_self)); - let vis = ty::Visibility::Public; self.resolver.define(parent, item.ident, ns, (def, vis, item.span, self.expansion)); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 6ea666e21dcdd..f1be821d526e1 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -43,7 +43,7 @@ use rustc::middle::cstore::CrateLoader; use rustc::session::Session; use rustc::lint; use rustc::hir::def::*; -use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, LOCAL_CRATE, DefId}; +use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, DefId}; use rustc::ty; use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap}; @@ -51,7 +51,7 @@ use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap}; use syntax::ext::hygiene::{Mark, SyntaxContext}; use syntax::ast::{self, Name, NodeId, Ident, SpannedIdent, FloatTy, IntTy, UintTy}; use syntax::ext::base::SyntaxExtension; -use syntax::ext::base::Determinacy::{Determined, Undetermined}; +use syntax::ext::base::Determinacy::{self, Determined, Undetermined}; use syntax::ext::base::MacroKind; use syntax::symbol::{Symbol, keywords}; use syntax::util::lev_distance::find_best_match_for_name; @@ -861,6 +861,8 @@ pub struct ModuleData<'a> { /// Span of the module itself. Used for error reporting. span: Span, + + expansion: Mark, } pub type Module<'a> = &'a ModuleData<'a>; @@ -869,6 +871,7 @@ impl<'a> ModuleData<'a> { fn new(parent: Option>, kind: ModuleKind, normal_ancestor_id: DefId, + expansion: Mark, span: Span) -> Self { ModuleData { parent: parent, @@ -884,6 +887,7 @@ impl<'a> ModuleData<'a> { traits: RefCell::new(None), populated: Cell::new(normal_ancestor_id.is_local()), span: span, + expansion: expansion, } } @@ -1058,6 +1062,13 @@ impl<'a> NameBinding<'a> { _ => true, } } + + fn is_macro_def(&self) -> bool { + match self.kind { + NameBindingKind::Def(Def::Macro(..)) => true, + _ => false, + } + } } /// Interns the names of the primitive types. @@ -1104,7 +1115,8 @@ pub struct Resolver<'a> { prelude: Option>, - trait_item_map: FxHashMap<(DefId, Name, Namespace), (Def, bool /* has self */)>, + // n.b. This is used only for better diagnostics, not name resolution itself. + has_self: FxHashSet, // Names of fields of an item `DefId` accessible with dot syntax. // Used for hints during error reporting. @@ -1127,7 +1139,7 @@ pub struct Resolver<'a> { label_ribs: Vec>, // The trait that the current context can refer to. - current_trait_ref: Option<(DefId, TraitRef)>, + current_trait_ref: Option<(Module<'a>, TraitRef)>, // The current self type if inside an impl (used for better errors). current_self_type: Option, @@ -1157,7 +1169,7 @@ pub struct Resolver<'a> { // entry block for `f`. block_map: NodeMap>, module_map: FxHashMap>, - extern_crate_roots: FxHashMap<(CrateNum, bool /* MacrosOnly? */), Module<'a>>, + extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>, pub make_glob_map: bool, // Maps imports to the names of items actually imported (this actually maps @@ -1177,9 +1189,9 @@ pub struct Resolver<'a> { use_extern_macros: bool, // true if `#![feature(use_extern_macros)]` crate_loader: &'a mut CrateLoader, - macro_names: FxHashSet, + macro_names: FxHashSet, global_macros: FxHashMap>, - lexical_macro_resolutions: Vec<(Name, &'a Cell>)>, + lexical_macro_resolutions: Vec<(Ident, &'a Cell>)>, macro_map: FxHashMap>, macro_defs: FxHashMap, local_macro_def_scopes: FxHashMap>, @@ -1302,13 +1314,13 @@ impl<'a> Resolver<'a> { let root_module_kind = ModuleKind::Def(Def::Mod(root_def_id), keywords::Invalid.name()); let graph_root = arenas.alloc_module(ModuleData { no_implicit_prelude: attr::contains_name(&krate.attrs, "no_implicit_prelude"), - ..ModuleData::new(None, root_module_kind, root_def_id, krate.span) + ..ModuleData::new(None, root_module_kind, root_def_id, Mark::root(), krate.span) }); let mut module_map = FxHashMap(); module_map.insert(DefId::local(CRATE_DEF_INDEX), graph_root); let mut definitions = Definitions::new(); - DefCollector::new(&mut definitions) + DefCollector::new(&mut definitions, Mark::root()) .collect_root(crate_name, &session.local_crate_disambiguator().as_str()); let mut invocations = FxHashMap(); @@ -1330,7 +1342,7 @@ impl<'a> Resolver<'a> { graph_root: graph_root, prelude: None, - trait_item_map: FxHashMap(), + has_self: FxHashSet(), field_names: FxHashMap(), determined_imports: Vec::new(), @@ -1356,7 +1368,7 @@ impl<'a> Resolver<'a> { trait_map: NodeMap(), module_map: module_map, block_map: NodeMap(), - extern_crate_roots: FxHashMap(), + extern_module_map: FxHashMap(), make_glob_map: make_glob_map == MakeGlobMap::Yes, glob_map: NodeMap(), @@ -1377,8 +1389,9 @@ impl<'a> Resolver<'a> { vis: ty::Visibility::Public, }), - // `#![feature(proc_macro)]` implies `#[feature(extern_macros)]` - use_extern_macros: features.use_extern_macros || features.proc_macro, + // The `proc_macro` and `decl_macro` features imply `use_extern_macros` + use_extern_macros: + features.use_extern_macros || features.proc_macro || features.decl_macro, crate_loader: crate_loader, macro_names: FxHashSet(), @@ -1440,9 +1453,11 @@ impl<'a> Resolver<'a> { parent: Module<'a>, kind: ModuleKind, normal_ancestor_id: DefId, + expansion: Mark, span: Span, ) -> Module<'a> { - self.arenas.alloc_module(ModuleData::new(Some(parent), kind, normal_ancestor_id, span)) + let module = ModuleData::new(Some(parent), kind, normal_ancestor_id, expansion, span); + self.arenas.alloc_module(module) } fn record_use(&mut self, ident: Ident, ns: Namespace, binding: &'a NameBinding<'a>, span: Span) @@ -1504,10 +1519,15 @@ impl<'a> Resolver<'a> { path_span: Span) -> Option> { if ns == TypeNS { - ident = ident.unhygienize(); + ident.ctxt = if ident.name == keywords::SelfType.name() { + SyntaxContext::empty() // FIXME(jseyfried) improve `Self` hygiene + } else { + ident.ctxt.modern() + } } // Walk backwards up the ribs in scope. + let mut module = self.graph_root; for i in (0 .. self.ribs[ns].len()).rev() { if let Some(def) = self.ribs[ns][i].bindings.get(&ident).cloned() { // The ident resolves to a type parameter or local variable. @@ -1516,45 +1536,120 @@ impl<'a> Resolver<'a> { )); } - if let ModuleRibKind(module) = self.ribs[ns][i].kind { - let item = self.resolve_ident_in_module(module, ident, ns, false, - record_used, path_span); - if let Ok(binding) = item { - // The ident resolves to an item. - return Some(LexicalScopeBinding::Item(binding)); + module = match self.ribs[ns][i].kind { + ModuleRibKind(module) => module, + MacroDefinition(def) if def == self.macro_defs[&ident.ctxt.outer()] => { + // If an invocation of this macro created `ident`, give up on `ident` + // and switch to `ident`'s source from the macro definition. + ident.ctxt.remove_mark(); + continue } + _ => continue, + }; - if let ModuleKind::Block(..) = module.kind { // We can see through blocks - } else if !module.no_implicit_prelude { - return self.prelude.and_then(|prelude| { - self.resolve_ident_in_module(prelude, ident, ns, false, - false, path_span).ok() - }).map(LexicalScopeBinding::Item) - } else { - return None; - } + let item = self.resolve_ident_in_module_unadjusted( + module, ident, ns, false, record_used, path_span, + ); + if let Ok(binding) = item { + // The ident resolves to an item. + return Some(LexicalScopeBinding::Item(binding)); } - if let MacroDefinition(def) = self.ribs[ns][i].kind { - // If an invocation of this macro created `ident`, give up on `ident` - // and switch to `ident`'s source from the macro definition. - let ctxt_data = ident.ctxt.data(); - if def == self.macro_defs[&ctxt_data.outer_mark] { - ident.ctxt = ctxt_data.prev_ctxt; - } + match module.kind { + ModuleKind::Block(..) => {}, // We can see through blocks + _ => break, } } + ident.ctxt = ident.ctxt.modern(); + loop { + module = unwrap_or!(self.hygienic_lexical_parent(module, &mut ident.ctxt), break); + let orig_current_module = self.current_module; + self.current_module = module; // Lexical resolutions can never be a privacy error. + let result = self.resolve_ident_in_module_unadjusted( + module, ident, ns, false, record_used, path_span, + ); + self.current_module = orig_current_module; + + match result { + Ok(binding) => return Some(LexicalScopeBinding::Item(binding)), + Err(Undetermined) => return None, + Err(Determined) => {} + } + } + + match self.prelude { + Some(prelude) if !module.no_implicit_prelude => { + self.resolve_ident_in_module_unadjusted(prelude, ident, ns, false, false, path_span) + .ok().map(LexicalScopeBinding::Item) + } + _ => None, + } + } + + fn hygienic_lexical_parent(&mut self, mut module: Module<'a>, ctxt: &mut SyntaxContext) + -> Option> { + if !module.expansion.is_descendant_of(ctxt.outer()) { + return Some(self.macro_def_scope(ctxt.remove_mark())); + } + + if let ModuleKind::Block(..) = module.kind { + return Some(module.parent.unwrap()); + } + + let mut module_expansion = module.expansion.modern(); // for backward compatability + while let Some(parent) = module.parent { + let parent_expansion = parent.expansion.modern(); + if module_expansion.is_descendant_of(parent_expansion) && + parent_expansion != module_expansion { + return if parent_expansion.is_descendant_of(ctxt.outer()) { + Some(parent) + } else { + None + }; + } + module = parent; + module_expansion = parent_expansion; + } + None } - fn resolve_crate_var(&mut self, crate_var_ctxt: SyntaxContext, span: Span) -> Module<'a> { - let mut ctxt_data = crate_var_ctxt.data(); - while ctxt_data.prev_ctxt != SyntaxContext::empty() { - ctxt_data = ctxt_data.prev_ctxt.data(); + fn resolve_ident_in_module(&mut self, + module: Module<'a>, + mut ident: Ident, + ns: Namespace, + ignore_unresolved_invocations: bool, + record_used: bool, + span: Span) + -> Result<&'a NameBinding<'a>, Determinacy> { + ident.ctxt = ident.ctxt.modern(); + let orig_current_module = self.current_module; + if let Some(def) = ident.ctxt.adjust(module.expansion) { + self.current_module = self.macro_def_scope(def); + } + let result = self.resolve_ident_in_module_unadjusted( + module, ident, ns, ignore_unresolved_invocations, record_used, span, + ); + self.current_module = orig_current_module; + result + } + + fn resolve_crate_root(&mut self, mut ctxt: SyntaxContext) -> Module<'a> { + let module = match ctxt.adjust(Mark::root()) { + Some(def) => self.macro_def_scope(def), + None => return self.graph_root, + }; + self.get_module(DefId { index: CRATE_DEF_INDEX, ..module.normal_ancestor_id }) + } + + fn resolve_self(&mut self, ctxt: &mut SyntaxContext, module: Module<'a>) -> Module<'a> { + let mut module = self.get_module(module.normal_ancestor_id); + while module.span.ctxt.modern() != *ctxt { + let parent = module.parent.unwrap_or_else(|| self.macro_def_scope(ctxt.remove_mark())); + module = self.get_module(parent.normal_ancestor_id); } - let module = self.macro_def_scope(ctxt_data.outer_mark, span); - if module.is_local() { self.graph_root } else { module } + module } // AST resolution @@ -1602,15 +1697,12 @@ impl<'a> Resolver<'a> { fn search_label(&self, mut ident: Ident) -> Option { for rib in self.label_ribs.iter().rev() { match rib.kind { - NormalRibKind => { - // Continue - } + NormalRibKind => {} + // If an invocation of this macro created `ident`, give up on `ident` + // and switch to `ident`'s source from the macro definition. MacroDefinition(def) => { - // If an invocation of this macro created `ident`, give up on `ident` - // and switch to `ident`'s source from the macro definition. - let ctxt_data = ident.ctxt.data(); - if def == self.macro_defs[&ctxt_data.outer_mark] { - ident.ctxt = ctxt_data.prev_ctxt; + if def == self.macro_defs[&ident.ctxt.outer()] { + ident.ctxt.remove_mark(); } } _ => { @@ -1742,22 +1834,21 @@ impl<'a> Resolver<'a> { let mut function_type_rib = Rib::new(rib_kind); let mut seen_bindings = FxHashMap(); for type_parameter in &generics.ty_params { - let name = type_parameter.ident.name; + let ident = type_parameter.ident.modern(); debug!("with_type_parameter_rib: {}", type_parameter.id); - if seen_bindings.contains_key(&name) { - let span = seen_bindings.get(&name).unwrap(); - resolve_error(self, - type_parameter.span, - ResolutionError::NameAlreadyUsedInTypeParameterList(name, - span)); + if seen_bindings.contains_key(&ident) { + let span = seen_bindings.get(&ident).unwrap(); + let err = + ResolutionError::NameAlreadyUsedInTypeParameterList(ident.name, span); + resolve_error(self, type_parameter.span, err); } - seen_bindings.entry(name).or_insert(type_parameter.span); + seen_bindings.entry(ident).or_insert(type_parameter.span); // plain insert (no renaming) let def_id = self.definitions.local_def_id(type_parameter.id); let def = Def::TyParam(def_id); - function_type_rib.bindings.insert(Ident::with_empty_ctxt(name), def); + function_type_rib.bindings.insert(ident, def); self.record_def(type_parameter.id, PathResolution::new(def)); } self.ribs[TypeNS].push(function_type_rib); @@ -1817,11 +1908,20 @@ impl<'a> Resolver<'a> { let mut new_val = None; let mut new_id = None; if let Some(trait_ref) = opt_trait_ref { - let def = self.smart_resolve_path(trait_ref.ref_id, None, - &trait_ref.path, PathSource::Trait).base_def(); + let path: Vec<_> = trait_ref.path.segments.iter().map(|seg| seg.identifier).collect(); + let def = self.smart_resolve_path_fragment(trait_ref.ref_id, + None, + &path, + trait_ref.path.span, + trait_ref.path.segments.last().unwrap().span, + PathSource::Trait) + .base_def(); if def != Def::Err { - new_val = Some((def.def_id(), trait_ref.clone())); new_id = Some(def.def_id()); + let span = trait_ref.path.span; + if let PathResult::Module(module) = self.resolve_path(&path, None, false, span) { + new_val = Some((module, trait_ref.clone())); + } } } let original_trait_ref = replace(&mut self.current_trait_ref, new_val); @@ -1872,7 +1972,7 @@ impl<'a> Resolver<'a> { ImplItemKind::Const(..) => { // If this is a trait impl, ensure the const // exists in trait - this.check_trait_item(impl_item.ident.name, + this.check_trait_item(impl_item.ident, ValueNS, impl_item.span, |n, s| ResolutionError::ConstNotMemberOfTrait(n, s)); @@ -1881,7 +1981,7 @@ impl<'a> Resolver<'a> { ImplItemKind::Method(ref sig, _) => { // If this is a trait impl, ensure the method // exists in trait - this.check_trait_item(impl_item.ident.name, + this.check_trait_item(impl_item.ident, ValueNS, impl_item.span, |n, s| ResolutionError::MethodNotMemberOfTrait(n, s)); @@ -1898,7 +1998,7 @@ impl<'a> Resolver<'a> { ImplItemKind::Type(ref ty) => { // If this is a trait impl, ensure the type // exists in trait - this.check_trait_item(impl_item.ident.name, + this.check_trait_item(impl_item.ident, TypeNS, impl_item.span, |n, s| ResolutionError::TypeNotMemberOfTrait(n, s)); @@ -1916,15 +2016,15 @@ impl<'a> Resolver<'a> { }); } - fn check_trait_item(&self, name: Name, ns: Namespace, span: Span, err: F) + fn check_trait_item(&mut self, ident: Ident, ns: Namespace, span: Span, err: F) where F: FnOnce(Name, &str) -> ResolutionError { // If there is a TraitRef in scope for an impl, then the method must be in the // trait. - if let Some((did, ref trait_ref)) = self.current_trait_ref { - if !self.trait_item_map.contains_key(&(did, name, ns)) { - let path_str = path_names_to_string(&trait_ref.path); - resolve_error(self, span, err(name, &path_str)); + if let Some((module, _)) = self.current_trait_ref { + if self.resolve_ident_in_module(module, ident, ns, false, false, span).is_err() { + let path = &self.current_trait_ref.as_ref().unwrap().1.path; + resolve_error(self, span, err(ident.name, &path_names_to_string(path))); } } } @@ -2294,15 +2394,16 @@ impl<'a> Resolver<'a> { } // Try to lookup the name in more relaxed fashion for better error reporting. - let name = path.last().unwrap().name; - let candidates = this.lookup_import_candidates(name, ns, is_expected); + let ident = *path.last().unwrap(); + let candidates = this.lookup_import_candidates(ident.name, ns, is_expected); if !candidates.is_empty() { let mut module_span = this.current_module.span; module_span.hi = module_span.lo; // Report import candidates as help and proceed searching for labels. show_candidates(&mut err, module_span, &candidates, def.is_some()); } else if is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) { - let enum_candidates = this.lookup_import_candidates(name, ns, is_enum_variant); + let enum_candidates = + this.lookup_import_candidates(ident.name, ns, is_enum_variant); let mut enum_candidates = enum_candidates.iter() .map(|suggestion| import_candidate_to_paths(&suggestion)).collect::>(); enum_candidates.sort(); @@ -2318,7 +2419,7 @@ impl<'a> Resolver<'a> { } } if path.len() == 1 && this.self_type_is_available(span) { - if let Some(candidate) = this.lookup_assoc_candidate(name, ns, is_expected) { + if let Some(candidate) = this.lookup_assoc_candidate(ident, ns, is_expected) { let self_is_available = this.self_value_is_available(path[0].ctxt, span); match candidate { AssocSuggestion::Field => { @@ -2432,7 +2533,7 @@ impl<'a> Resolver<'a> { // or `::A::B`. If `B` should be resolved in value namespace then // it needs to be added to the trait map. if ns == ValueNS { - let item_name = path.last().unwrap().name; + let item_name = *path.last().unwrap(); let traits = self.get_traits_containing_item(item_name, ns); self.trait_map.insert(id, traits); } @@ -2486,7 +2587,7 @@ impl<'a> Resolver<'a> { } let is_global = self.global_macros.get(&path[0].name).cloned() .map(|binding| binding.get_macro(self).kind() == MacroKind::Bang).unwrap_or(false); - if primary_ns != MacroNS && (is_global || self.macro_names.contains(&path[0].name)) { + if primary_ns != MacroNS && (is_global || self.macro_names.contains(&path[0].modern())) { // Return some dummy definition, it's enough for error reporting. return Some( PathResolution::new(Def::Macro(DefId::local(CRATE_DEF_INDEX), MacroKind::Bang)) @@ -2595,13 +2696,17 @@ impl<'a> Resolver<'a> { let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; if i == 0 && ns == TypeNS && ident.name == keywords::SelfValue.name() { - module = Some(self.module_map[&self.current_module.normal_ancestor_id]); + let mut ctxt = ident.ctxt.modern(); + module = Some(self.resolve_self(&mut ctxt, self.current_module)); continue } else if allow_super && ns == TypeNS && ident.name == keywords::Super.name() { - let current_module = if i == 0 { self.current_module } else { module.unwrap() }; - let self_module = self.module_map[¤t_module.normal_ancestor_id]; + let mut ctxt = ident.ctxt.modern(); + let self_module = match i { + 0 => self.resolve_self(&mut ctxt, self.current_module), + _ => module.unwrap(), + }; if let Some(parent) = self_module.parent { - module = Some(self.module_map[&parent.normal_ancestor_id]); + module = Some(self.resolve_self(&mut ctxt, parent)); continue } else { let msg = "There are too many initial `super`s.".to_string(); @@ -2611,10 +2716,10 @@ impl<'a> Resolver<'a> { allow_super = false; if i == 0 && ns == TypeNS && ident.name == keywords::CrateRoot.name() { - module = Some(self.graph_root); + module = Some(self.resolve_crate_root(ident.ctxt.modern())); continue } else if i == 0 && ns == TypeNS && ident.name == "$crate" { - module = Some(self.resolve_crate_var(ident.ctxt, path_span)); + module = Some(self.resolve_crate_root(ident.ctxt)); continue } @@ -2811,7 +2916,7 @@ impl<'a> Resolver<'a> { } fn lookup_assoc_candidate(&mut self, - name: Name, + ident: Ident, ns: Namespace, filter_fn: FilterFn) -> Option @@ -2837,7 +2942,7 @@ impl<'a> Resolver<'a> { Def::Struct(did) | Def::Union(did) if resolution.unresolved_segments() == 0 => { if let Some(field_names) = self.field_names.get(&did) { - if field_names.iter().any(|&field_name| name == field_name) { + if field_names.iter().any(|&field_name| ident.name == field_name) { return Some(AssocSuggestion::Field); } } @@ -2849,10 +2954,12 @@ impl<'a> Resolver<'a> { } // Look for associated items in the current trait. - if let Some((trait_did, _)) = self.current_trait_ref { - if let Some(&(def, has_self)) = self.trait_item_map.get(&(trait_did, name, ns)) { + if let Some((module, _)) = self.current_trait_ref { + if let Ok(binding) = + self.resolve_ident_in_module(module, ident, ns, false, false, module.span) { + let def = binding.def(); if filter_fn(def) { - return Some(if has_self { + return Some(if self.has_self.contains(&def.def_id()) { AssocSuggestion::MethodWithSelf } else { AssocSuggestion::AssocItem @@ -3073,13 +3180,13 @@ impl<'a> Resolver<'a> { // field, we need to add any trait methods we find that match // the field name so that we can do some nice error reporting // later on in typeck. - let traits = self.get_traits_containing_item(name.node.name, ValueNS); + let traits = self.get_traits_containing_item(name.node, ValueNS); self.trait_map.insert(expr.id, traits); } ExprKind::MethodCall(name, ..) => { debug!("(recording candidate traits for expr) recording traits for {}", expr.id); - let traits = self.get_traits_containing_item(name.node.name, ValueNS); + let traits = self.get_traits_containing_item(name.node, ValueNS); self.trait_map.insert(expr.id, traits); } _ => { @@ -3088,29 +3195,30 @@ impl<'a> Resolver<'a> { } } - fn get_traits_containing_item(&mut self, name: Name, ns: Namespace) -> Vec { - debug!("(getting traits containing item) looking for '{}'", name); + fn get_traits_containing_item(&mut self, mut ident: Ident, ns: Namespace) + -> Vec { + debug!("(getting traits containing item) looking for '{}'", ident.name); let mut found_traits = Vec::new(); // Look for the current trait. - if let Some((trait_def_id, _)) = self.current_trait_ref { - if self.trait_item_map.contains_key(&(trait_def_id, name, ns)) { - found_traits.push(TraitCandidate { def_id: trait_def_id, import_id: None }); + if let Some((module, _)) = self.current_trait_ref { + if self.resolve_ident_in_module(module, ident, ns, false, false, module.span).is_ok() { + let def_id = module.def_id().unwrap(); + found_traits.push(TraitCandidate { def_id: def_id, import_id: None }); } } + ident.ctxt = ident.ctxt.modern(); let mut search_module = self.current_module; loop { - self.get_traits_in_module_containing_item(name, ns, search_module, &mut found_traits); - match search_module.kind { - ModuleKind::Block(..) => search_module = search_module.parent.unwrap(), - _ => break, - } + self.get_traits_in_module_containing_item(ident, ns, search_module, &mut found_traits); + search_module = + unwrap_or!(self.hygienic_lexical_parent(search_module, &mut ident.ctxt), break); } if let Some(prelude) = self.prelude { if !search_module.no_implicit_prelude { - self.get_traits_in_module_containing_item(name, ns, prelude, &mut found_traits); + self.get_traits_in_module_containing_item(ident, ns, prelude, &mut found_traits); } } @@ -3118,9 +3226,9 @@ impl<'a> Resolver<'a> { } fn get_traits_in_module_containing_item(&mut self, - name: Name, + ident: Ident, ns: Namespace, - module: Module, + module: Module<'a>, found_traits: &mut Vec) { let mut traits = module.traits.borrow_mut(); if traits.is_none() { @@ -3135,8 +3243,13 @@ impl<'a> Resolver<'a> { } for &(trait_name, binding) in traits.as_ref().unwrap().iter() { - let trait_def_id = binding.def().def_id(); - if self.trait_item_map.contains_key(&(trait_def_id, name, ns)) { + let module = binding.module().unwrap(); + let mut ident = ident; + if ident.ctxt.glob_adjust(module.expansion, binding.span.ctxt.modern()).is_none() { + continue + } + if self.resolve_ident_in_module_unadjusted(module, ident, ns, false, false, module.span) + .is_ok() { let import_id = match binding.kind { NameBindingKind::Import { directive, .. } => { self.maybe_unused_trait_imports.insert(directive.id); @@ -3145,6 +3258,7 @@ impl<'a> Resolver<'a> { } _ => None, }; + let trait_def_id = module.def_id().unwrap(); found_traits.push(TraitCandidate { def_id: trait_def_id, import_id: import_id }); } } @@ -3326,15 +3440,15 @@ impl<'a> Resolver<'a> { } fn report_shadowing_errors(&mut self) { - for (name, scope) in replace(&mut self.lexical_macro_resolutions, Vec::new()) { - self.resolve_legacy_scope(scope, name, true); + for (ident, scope) in replace(&mut self.lexical_macro_resolutions, Vec::new()) { + self.resolve_legacy_scope(scope, ident, true); } let mut reported_errors = FxHashSet(); for binding in replace(&mut self.disallowed_shadowing, Vec::new()) { - if self.resolve_legacy_scope(&binding.parent, binding.name, false).is_some() && - reported_errors.insert((binding.name, binding.span)) { - let msg = format!("`{}` is already in scope", binding.name); + if self.resolve_legacy_scope(&binding.parent, binding.ident, false).is_some() && + reported_errors.insert((binding.ident, binding.span)) { + let msg = format!("`{}` is already in scope", binding.ident); self.session.struct_span_err(binding.span, &msg) .note("macro-expanded `macro_rules!`s may not shadow \ existing macros (see RFC 1560)") diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 231d30cd2a22d..9a37df7623279 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -76,7 +76,7 @@ pub enum LegacyScope<'a> { pub struct LegacyBinding<'a> { pub parent: Cell>, - pub name: ast::Name, + pub ident: Ident, def_id: DefId, pub span: Span, } @@ -110,7 +110,7 @@ impl<'a> base::Resolver for Resolver<'a> { } fn get_module_scope(&mut self, id: ast::NodeId) -> Mark { - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); let module = self.module_map[&self.definitions.local_def_id(id)]; self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData { module: Cell::new(module), @@ -130,7 +130,7 @@ impl<'a> base::Resolver for Resolver<'a> { let ident = path.segments[0].identifier; if ident.name == "$crate" { path.segments[0].identifier.name = keywords::CrateRoot.name(); - let module = self.0.resolve_crate_var(ident.ctxt, self.1); + let module = self.0.resolve_crate_root(ident.ctxt); if !module.is_local() { let span = path.segments[0].span; path.segments.insert(1, match module.kind { @@ -158,7 +158,7 @@ impl<'a> base::Resolver for Resolver<'a> { fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion, derives: &[Mark]) { let invocation = self.invocations[&mark]; - self.collect_def_ids(invocation, expansion); + self.collect_def_ids(mark, invocation, expansion); self.current_module = invocation.module.get(); self.current_module.unresolved_invocations.borrow_mut().remove(&mark); @@ -290,9 +290,18 @@ impl<'a> base::Resolver for Resolver<'a> { Err(determinacy) => return Err(determinacy), }, }; + self.macro_defs.insert(invoc.expansion_data.mark, def.def_id()); + let normal_module_def_id = + self.macro_def_scope(invoc.expansion_data.mark).normal_ancestor_id; + self.definitions.add_macro_def_scope(invoc.expansion_data.mark, normal_module_def_id); + self.unused_macros.remove(&def.def_id()); - Ok(Some(self.get_macro(def))) + let ext = self.get_macro(def); + if ext.is_modern() { + invoc.expansion_data.mark.set_modern(); + } + Ok(Some(ext)) } fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool) @@ -416,8 +425,7 @@ impl<'a> Resolver<'a> { return def; } - let name = path[0].name; - let legacy_resolution = self.resolve_legacy_scope(&invocation.legacy_scope, name, false); + let legacy_resolution = self.resolve_legacy_scope(&invocation.legacy_scope, path[0], false); let result = if let Some(MacroBinding::Legacy(binding)) = legacy_resolution { Ok(Def::Macro(binding.def_id, MacroKind::Bang)) } else { @@ -439,26 +447,31 @@ impl<'a> Resolver<'a> { // Resolve the initial segment of a non-global macro path (e.g. `foo` in `foo::bar!();`) pub fn resolve_lexical_macro_path_segment(&mut self, - ident: Ident, + mut ident: Ident, ns: Namespace, record_used: bool, path_span: Span) -> Result, Determinacy> { + ident = ident.modern(); let mut module = Some(self.current_module); let mut potential_illegal_shadower = Err(Determinacy::Determined); let determinacy = if record_used { Determinacy::Determined } else { Determinacy::Undetermined }; loop { + let orig_current_module = self.current_module; let result = if let Some(module) = module { + self.current_module = module; // Lexical resolutions can never be a privacy error. // Since expanded macros may not shadow the lexical scope and // globs may not shadow global macros (both enforced below), // we resolve with restricted shadowing (indicated by the penultimate argument). - self.resolve_ident_in_module(module, ident, ns, true, record_used, path_span) - .map(MacroBinding::Modern) + self.resolve_ident_in_module_unadjusted( + module, ident, ns, true, record_used, path_span, + ).map(MacroBinding::Modern) } else { self.global_macros.get(&ident.name).cloned().ok_or(determinacy) .map(MacroBinding::Global) }; + self.current_module = orig_current_module; match result.map(MacroBinding::binding) { Ok(binding) => { @@ -491,10 +504,7 @@ impl<'a> Resolver<'a> { } module = match module { - Some(module) => match module.kind { - ModuleKind::Block(..) => module.parent, - ModuleKind::Def(..) => None, - }, + Some(module) => self.hygienic_lexical_parent(module, &mut ident.ctxt), None => return potential_illegal_shadower, } } @@ -502,9 +512,10 @@ impl<'a> Resolver<'a> { pub fn resolve_legacy_scope(&mut self, mut scope: &'a Cell>, - name: Name, + ident: Ident, record_used: bool) -> Option> { + let ident = ident.modern(); let mut possible_time_travel = None; let mut relative_depth: u32 = 0; let mut binding = None; @@ -531,7 +542,7 @@ impl<'a> Resolver<'a> { scope = &invocation.legacy_scope; } LegacyScope::Binding(potential_binding) => { - if potential_binding.name == name { + if potential_binding.ident == ident { if (!self.use_extern_macros || record_used) && relative_depth > 0 { self.disallowed_shadowing.push(potential_binding); } @@ -545,9 +556,9 @@ impl<'a> Resolver<'a> { let binding = if let Some(binding) = binding { MacroBinding::Legacy(binding) - } else if let Some(binding) = self.global_macros.get(&name).cloned() { + } else if let Some(binding) = self.global_macros.get(&ident.name).cloned() { if !self.use_extern_macros { - self.record_use(Ident::with_empty_ctxt(name), MacroNS, binding, DUMMY_SP); + self.record_use(ident, MacroNS, binding, DUMMY_SP); } MacroBinding::Global(binding) } else { @@ -557,7 +568,7 @@ impl<'a> Resolver<'a> { if !self.use_extern_macros { if let Some(scope) = possible_time_travel { // Check for disallowed shadowing later - self.lexical_macro_resolutions.push((name, scope)); + self.lexical_macro_resolutions.push((ident, scope)); } } @@ -578,7 +589,7 @@ impl<'a> Resolver<'a> { for &(mark, ident, span, kind) in module.legacy_macro_resolutions.borrow().iter() { let legacy_scope = &self.invocations[&mark].legacy_scope; - let legacy_resolution = self.resolve_legacy_scope(legacy_scope, ident.name, true); + let legacy_resolution = self.resolve_legacy_scope(legacy_scope, ident, true); let resolution = self.resolve_lexical_macro_path_segment(ident, MacroNS, true, span); match (legacy_resolution, resolution) { (Some(MacroBinding::Legacy(legacy_binding)), Ok(MacroBinding::Modern(binding))) => { @@ -615,7 +626,7 @@ impl<'a> Resolver<'a> { err: &mut DiagnosticBuilder<'a>, span: Span) { // First check if this is a locally-defined bang macro. let suggestion = if let MacroKind::Bang = kind { - find_best_match_for_name(self.macro_names.iter(), name, None) + find_best_match_for_name(self.macro_names.iter().map(|ident| &ident.name), name, None) } else { None // Then check global macros. @@ -659,7 +670,10 @@ impl<'a> Resolver<'a> { } } - fn collect_def_ids(&mut self, invocation: &'a InvocationData<'a>, expansion: &Expansion) { + fn collect_def_ids(&mut self, + mark: Mark, + invocation: &'a InvocationData<'a>, + expansion: &Expansion) { let Resolver { ref mut invocations, arenas, graph_root, .. } = *self; let InvocationData { def_index, const_expr, .. } = *invocation; @@ -675,7 +689,7 @@ impl<'a> Resolver<'a> { }); }; - let mut def_collector = DefCollector::new(&mut self.definitions); + let mut def_collector = DefCollector::new(&mut self.definitions, mark); def_collector.visit_macro_invoc = Some(visit_macro_invoc); def_collector.with_parent(def_index, |def_collector| { if const_expr { @@ -687,7 +701,10 @@ impl<'a> Resolver<'a> { }); } - pub fn define_macro(&mut self, item: &ast::Item, legacy_scope: &mut LegacyScope<'a>) { + pub fn define_macro(&mut self, + item: &ast::Item, + expansion: Mark, + legacy_scope: &mut LegacyScope<'a>) { self.local_macro_def_scopes.insert(item.id, self.current_module); let ident = item.ident; if ident.name == "macro_rules" { @@ -699,16 +716,26 @@ impl<'a> Resolver<'a> { &self.session.features, item)); self.macro_map.insert(def_id, ext); - *legacy_scope = LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding { - parent: Cell::new(*legacy_scope), name: ident.name, def_id: def_id, span: item.span, - })); - self.macro_names.insert(ident.name); - if attr::contains_name(&item.attrs, "macro_export") { - let def = Def::Macro(def_id, MacroKind::Bang); - self.macro_exports.push(Export { name: ident.name, def: def, span: item.span }); + let def = match item.node { ast::ItemKind::MacroDef(ref def) => def, _ => unreachable!() }; + if def.legacy { + let ident = ident.modern(); + self.macro_names.insert(ident); + *legacy_scope = LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding { + parent: Cell::new(*legacy_scope), ident: ident, def_id: def_id, span: item.span, + })); + if attr::contains_name(&item.attrs, "macro_export") { + let def = Def::Macro(def_id, MacroKind::Bang); + self.macro_exports + .push(Export { ident: ident.modern(), def: def, span: item.span }); + } else { + self.unused_macros.insert(def_id); + } } else { - self.unused_macros.insert(def_id); + let module = self.current_module; + let def = Def::Macro(def_id, MacroKind::Bang); + let vis = self.resolve_visibility(&item.vis); + self.define(module, ident, MacroNS, (def, vis, item.span, expansion)); } } diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 1d4ba4ed100b7..c077f507932c6 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -134,21 +134,20 @@ impl<'a> NameResolution<'a> { impl<'a> Resolver<'a> { fn resolution(&self, module: Module<'a>, ident: Ident, ns: Namespace) -> &'a RefCell> { - let ident = ident.unhygienize(); - *module.resolutions.borrow_mut().entry((ident, ns)) + *module.resolutions.borrow_mut().entry((ident.modern(), ns)) .or_insert_with(|| self.arenas.alloc_name_resolution()) } /// Attempts to resolve `ident` in namespaces `ns` of `module`. /// Invariant: if `record_used` is `Some`, import resolution must be complete. - pub fn resolve_ident_in_module(&mut self, - module: Module<'a>, - ident: Ident, - ns: Namespace, - restricted_shadowing: bool, - record_used: bool, - path_span: Span) - -> Result<&'a NameBinding<'a>, Determinacy> { + pub fn resolve_ident_in_module_unadjusted(&mut self, + module: Module<'a>, + ident: Ident, + ns: Namespace, + restricted_shadowing: bool, + record_used: bool, + path_span: Span) + -> Result<&'a NameBinding<'a>, Determinacy> { self.populate_module_if_necessary(module); let resolution = self.resolution(module, ident, ns) @@ -233,20 +232,22 @@ impl<'a> Resolver<'a> { return Err(Determined); } for directive in module.globs.borrow().iter() { - if self.is_accessible(directive.vis.get()) { - if let Some(module) = directive.imported_module.get() { - let result = self.resolve_ident_in_module(module, - ident, - ns, - false, - false, - path_span); - if let Err(Undetermined) = result { - return Err(Undetermined); - } - } else { - return Err(Undetermined); - } + if !self.is_accessible(directive.vis.get()) { + continue + } + let module = unwrap_or!(directive.imported_module.get(), return Err(Undetermined)); + let (orig_current_module, mut ident) = (self.current_module, ident.modern()); + match ident.ctxt.glob_adjust(module.expansion, directive.span.ctxt.modern()) { + Some(Some(def)) => self.current_module = self.macro_def_scope(def), + Some(None) => {} + None => continue, + }; + let result = self.resolve_ident_in_module_unadjusted( + module, ident, ns, false, false, path_span, + ); + self.current_module = orig_current_module; + if let Err(Undetermined) = result { + return Err(Undetermined); } } @@ -394,7 +395,14 @@ impl<'a> Resolver<'a> { // Define `binding` in `module`s glob importers. for directive in module.glob_importers.borrow_mut().iter() { - if self.is_accessible_from(binding.vis, directive.parent) { + let mut ident = ident.modern(); + let scope = match ident.ctxt.reverse_glob_adjust(module.expansion, + directive.span.ctxt.modern()) { + Some(Some(def)) => self.macro_def_scope(def), + Some(None) => directive.parent, + None => continue, + }; + if self.is_accessible_from(binding.vis, scope) { let imported_binding = self.import(binding, directive); let _ = self.try_define(directive.parent, ident, ns, imported_binding); } @@ -767,8 +775,14 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { let bindings = module.resolutions.borrow().iter().filter_map(|(&ident, resolution)| { resolution.borrow().binding().map(|binding| (ident, binding)) }).collect::>(); - for ((ident, ns), binding) in bindings { - if binding.pseudo_vis() == ty::Visibility::Public || self.is_accessible(binding.vis) { + for ((mut ident, ns), binding) in bindings { + let scope = match ident.ctxt.reverse_glob_adjust(module.expansion, + directive.span.ctxt.modern()) { + Some(Some(def)) => self.macro_def_scope(def), + Some(None) => self.current_module, + None => continue, + }; + if self.is_accessible_from(binding.pseudo_vis(), scope) { let imported_binding = self.import(binding, directive); let _ = self.try_define(directive.parent, ident, ns, imported_binding); } @@ -789,7 +803,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { if module as *const _ == self.graph_root as *const _ { let macro_exports = mem::replace(&mut self.macro_exports, Vec::new()); for export in macro_exports.into_iter().rev() { - if exported_macro_names.insert(export.name, export.span).is_none() { + if exported_macro_names.insert(export.ident.modern(), export.span).is_none() { reexports.push(export); } } @@ -803,14 +817,14 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { }; if binding.vis == ty::Visibility::Public && - (binding.is_import() || binding.is_extern_crate()) { + (binding.is_import() || binding.is_macro_def()) { let def = binding.def(); if def != Def::Err { if !def.def_id().is_local() { self.session.cstore.export_macros(def.def_id().krate); } if let Def::Macro(..) = def { - if let Some(&span) = exported_macro_names.get(&ident.name) { + if let Some(&span) = exported_macro_names.get(&ident.modern()) { let msg = format!("a macro named `{}` has already been exported", ident); self.session.struct_span_err(span, &msg) @@ -819,7 +833,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { .emit(); } } - reexports.push(Export { name: ident.name, def: def, span: binding.span }); + reexports.push(Export { ident: ident.modern(), def: def, span: binding.span }); } } diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 5a8acf9abe1d0..d83740936d5d4 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -828,7 +828,7 @@ fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String { if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() { sig.push('<'); sig.push_str(&generics.lifetimes.iter() - .map(|l| l.lifetime.name.to_string()) + .map(|l| l.lifetime.ident.name.to_string()) .collect::>() .join(", ")); if !generics.lifetimes.is_empty() { diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 9e8352fde8061..d1ff3ed4f490f 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -891,7 +891,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let item = tcx.associated_items(trait_did).find(|i| i.name == assoc_name) .expect("missing associated type"); let def = Def::AssociatedTy(item.def_id); - if !tcx.vis_is_accessible_from(item.vis, ref_id) { + let def_scope = tcx.adjust(assoc_name, item.container.id(), ref_id).1; + if !item.vis.is_accessible_from(def_scope, tcx) { let msg = format!("{} `{}` is private", def.kind_name(), assoc_name); tcx.sess.span_err(span, &msg); } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 4d5f67004a7cc..43bf702935ac3 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -338,6 +338,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// and return it, or `None`, if no such item was defined there. pub fn associated_item(&self, def_id: DefId, item_name: ast::Name) -> Option { - self.tcx.associated_items(def_id).find(|item| item.name == item_name) + let ident = self.tcx.adjust(item_name, def_id, self.body_id).0; + self.tcx.associated_items(def_id).find(|item| item.name.to_ident() == ident) } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 9ad72b2a137ea..b32eb9ac5fbf6 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -371,7 +371,13 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fn push_inherent_candidate(&mut self, xform_self_ty: Ty<'tcx>, item: ty::AssociatedItem, kind: CandidateKind<'tcx>, import_id: Option) { - if self.tcx.vis_is_accessible_from(item.vis, self.body_id) { + let is_accessible = if let LookingFor::MethodName(name) = self.looking_for { + let def_scope = self.tcx.adjust(name, item.container.id(), self.body_id).1; + item.vis.is_accessible_from(def_scope, self.tcx) + } else { + true + }; + if is_accessible { self.inherent_candidates.push(Candidate { xform_self_ty, item, kind, import_id }); } else if self.private_candidate.is_none() { self.private_candidate = Some(item.def()); @@ -380,7 +386,13 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fn push_extension_candidate(&mut self, xform_self_ty: Ty<'tcx>, item: ty::AssociatedItem, kind: CandidateKind<'tcx>, import_id: Option) { - if self.tcx.vis_is_accessible_from(item.vis, self.body_id) { + let is_accessible = if let LookingFor::MethodName(name) = self.looking_for { + let def_scope = self.tcx.adjust(name, item.container.id(), self.body_id).1; + item.vis.is_accessible_from(def_scope, self.tcx) + } else { + true + }; + if is_accessible { self.extension_candidates.push(Candidate { xform_self_ty, item, kind, import_id }); } else if self.private_candidate.is_none() { self.private_candidate = Some(item.def()); diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 7e70bb92cd6e0..edaaa863ecc34 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -195,8 +195,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; let field_ty = field.ty(tcx, substs); - - if tcx.vis_is_accessible_from(field.vis, self.body_id) { + let scope = self.tcx.hir.get_module_parent(self.body_id); + if field.vis.is_accessible_from(scope, self.tcx) { if self.is_fn_ty(&field_ty, span) { err.help(&format!("use `({0}.{1})(...)` if you \ meant to call the function \ diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 24a88140cf041..1ad3914854d34 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2916,9 +2916,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { match base_t.sty { ty::TyAdt(base_def, substs) if !base_def.is_enum() => { debug!("struct named {:?}", base_t); - if let Some(field) = base_def.struct_variant().find_field_named(field.node) { + let (ident, def_scope) = + self.tcx.adjust(field.node, base_def.did, self.body_id); + let fields = &base_def.struct_variant().fields; + if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) { let field_ty = self.field_ty(expr.span, field, substs); - if self.tcx.vis_is_accessible_from(field.vis, self.body_id) { + if field.vis.is_accessible_from(def_scope, self.tcx) { autoderef.finalize(lvalue_pref, base); self.apply_autoderef_adjustment(base.id, autoderefs, base_t); @@ -3024,16 +3027,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if !tuple_like { continue } debug!("tuple struct named {:?}", base_t); - base_def.struct_variant().fields.get(idx.node).and_then(|field| { + let ident = ast::Ident { + name: Symbol::intern(&idx.node.to_string()), + ctxt: idx.span.ctxt.modern(), + }; + let (ident, def_scope) = + self.tcx.adjust_ident(ident, base_def.did, self.body_id); + let fields = &base_def.struct_variant().fields; + if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) { let field_ty = self.field_ty(expr.span, field, substs); - private_candidate = Some((base_def.did, field_ty)); - if self.tcx.vis_is_accessible_from(field.vis, self.body_id) { + if field.vis.is_accessible_from(def_scope, self.tcx) { self.tcx.check_stability(field.did, expr.id, expr.span); Some(field_ty) } else { + private_candidate = Some((base_def.did, field_ty)); None } - }) + } else { + None + } } ty::TyTuple(ref v, _) => { tuple_like = true; @@ -3142,7 +3154,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut remaining_fields = FxHashMap(); for field in &variant.fields { - remaining_fields.insert(field.name, field); + remaining_fields.insert(field.name.to_ident(), field); } let mut seen_fields = FxHashMap(); @@ -3154,7 +3166,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let final_field_type; let field_type_hint; - if let Some(v_field) = remaining_fields.remove(&field.name.node) { + let ident = tcx.adjust(field.name.node, variant.did, self.body_id).0; + if let Some(v_field) = remaining_fields.remove(&ident) { final_field_type = self.field_ty(field.span, v_field, substs); field_type_hint = self.field_ty(field.span, v_field, hint_substs); @@ -3205,7 +3218,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut displayable_field_names = remaining_fields .keys() - .map(|x| x.as_str()) + .map(|ident| ident.name.as_str()) .collect::>(); displayable_field_names.sort(); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 141efe471af5f..5ea3eaa88d772 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -447,7 +447,7 @@ fn build_module(cx: &DocContext, did: DefId) -> clean::Module { let def_id = item.def.def_id(); if cx.tcx.sess.cstore.visibility(def_id) == ty::Visibility::Public { if !visited.insert(def_id) { continue } - if let Some(i) = try_inline(cx, item.def, item.name) { + if let Some(i) = try_inline(cx, item.def, item.ident.name) { items.extend(i) } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index d463e41c58a2a..39ebe490d0eb2 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -16,7 +16,6 @@ use std::mem; use syntax::abi; use syntax::ast; use syntax::attr; -use syntax::tokenstream::TokenStream; use syntax_pos::Span; use rustc::hir::map as hir_map; @@ -214,8 +213,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { LoadedMacro::ProcMacro(..) => continue, }; - let matchers = if let ast::ItemKind::MacroDef(ref tokens) = def.node { - let tts: Vec<_> = TokenStream::from(tokens.clone()).into_trees().collect(); + let matchers = if let ast::ItemKind::MacroDef(ref def) = def.node { + let tts: Vec<_> = def.stream().into_trees().collect(); tts.chunks(4).map(|arm| arm[0].span()).collect() } else { unreachable!() diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 24ce99208ed11..2eb39bc26b5cb 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -37,7 +37,7 @@ use std::u32; pub struct Lifetime { pub id: NodeId, pub span: Span, - pub name: Name + pub ident: Ident, } impl fmt::Debug for Lifetime { @@ -1019,6 +1019,18 @@ impl Mac_ { } } +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub struct MacroDef { + pub tokens: ThinTokenStream, + pub legacy: bool, +} + +impl MacroDef { + pub fn stream(&self) -> TokenStream { + self.tokens.clone().into() + } +} + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] pub enum StrStyle { /// A regular string, like `"foo"` @@ -1863,7 +1875,7 @@ pub enum ItemKind { Mac(Mac), /// A macro definition. - MacroDef(ThinTokenStream), + MacroDef(MacroDef), } impl ItemKind { diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs index 73aeb40df8400..2a5de3c7382a3 100644 --- a/src/libsyntax/diagnostics/plugin.rs +++ b/src/libsyntax/diagnostics/plugin.rs @@ -206,7 +206,7 @@ pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt, (descriptions.len(), ecx.expr_vec(span, descriptions)) }); - let static_ = ecx.lifetime(span, ecx.name_of("'static")); + let static_ = ecx.lifetime(span, Ident::from_str("'static")); let ty_str = ecx.ty_rptr( span, ecx.ty_ident(span, ecx.ident_of("str")), diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 1930f61121bb0..71dc81c375923 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -550,12 +550,16 @@ pub enum SyntaxExtension { /// An attribute-like procedural macro that derives a builtin trait. BuiltinDerive(BuiltinDeriveFn), + + /// A declarative macro, e.g. `macro m() {}`. + DeclMacro(Box, Option /* definition site span */), } impl SyntaxExtension { /// Return which kind of macro calls this syntax extension. pub fn kind(&self) -> MacroKind { match *self { + SyntaxExtension::DeclMacro(..) | SyntaxExtension::NormalTT(..) | SyntaxExtension::IdentTT(..) | SyntaxExtension::ProcMacro(..) => @@ -569,6 +573,13 @@ impl SyntaxExtension { MacroKind::Derive, } } + + pub fn is_modern(&self) -> bool { + match *self { + SyntaxExtension::DeclMacro(..) => true, + _ => false, + } + } } pub type NamedSyntaxExtension = (Name, SyntaxExtension); diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 09f22e8691e4c..a4580ea3939fb 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -76,10 +76,10 @@ pub trait AstBuilder { fn trait_ref(&self, path: ast::Path) -> ast::TraitRef; fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef; fn typarambound(&self, path: ast::Path) -> ast::TyParamBound; - fn lifetime(&self, span: Span, ident: ast::Name) -> ast::Lifetime; + fn lifetime(&self, span: Span, ident: ast::Ident) -> ast::Lifetime; fn lifetime_def(&self, span: Span, - name: ast::Name, + ident: ast::Ident, attrs: Vec, bounds: Vec) -> ast::LifetimeDef; @@ -478,19 +478,19 @@ impl<'a> AstBuilder for ExtCtxt<'a> { ast::TraitTyParamBound(self.poly_trait_ref(path.span, path), ast::TraitBoundModifier::None) } - fn lifetime(&self, span: Span, name: ast::Name) -> ast::Lifetime { - ast::Lifetime { id: ast::DUMMY_NODE_ID, span: span, name: name } + fn lifetime(&self, span: Span, ident: ast::Ident) -> ast::Lifetime { + ast::Lifetime { id: ast::DUMMY_NODE_ID, span: span, ident: ident } } fn lifetime_def(&self, span: Span, - name: ast::Name, + ident: ast::Ident, attrs: Vec, bounds: Vec) -> ast::LifetimeDef { ast::LifetimeDef { attrs: attrs.into(), - lifetime: self.lifetime(span, name), + lifetime: self.lifetime(span, ident), bounds: bounds } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 25e0aed220ab3..be077b481113f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -288,7 +288,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let derives = derives.entry(invoc.expansion_data.mark).or_insert_with(Vec::new); for path in &traits { - let mark = Mark::fresh(); + let mark = Mark::fresh(self.cx.current_expansion.mark); derives.push(mark); let item = match self.cx.resolver.resolve_macro( Mark::root(), path, MacroKind::Derive, false) { @@ -455,25 +455,37 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let path = &mac.node.path; let ident = ident.unwrap_or_else(|| keywords::Invalid.ident()); + let validate_and_set_expn_info = |def_site_span, allow_internal_unstable| { + if ident.name != keywords::Invalid.name() { + return Err(format!("macro {}! expects no ident argument, given '{}'", path, ident)); + } + mark.set_expn_info(ExpnInfo { + call_site: span, + callee: NameAndSpan { + format: MacroBang(Symbol::intern(&format!("{}", path))), + span: def_site_span, + allow_internal_unstable: allow_internal_unstable, + }, + }); + Ok(()) + }; + let marked_tts = noop_fold_tts(mac.node.stream(), &mut Marker(mark)); let opt_expanded = match *ext { - NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { - if ident.name != keywords::Invalid.name() { - let msg = - format!("macro {}! expects no ident argument, given '{}'", path, ident); + SyntaxExtension::DeclMacro(ref expand, def_site_span) => { + if let Err(msg) = validate_and_set_expn_info(def_site_span, false) { self.cx.span_err(path.span, &msg); return kind.dummy(span); } + kind.make_from(expand.expand(self.cx, span, marked_tts)) + } - invoc.expansion_data.mark.set_expn_info(ExpnInfo { - call_site: span, - callee: NameAndSpan { - format: MacroBang(Symbol::intern(&format!("{}", path))), - span: exp_span.map(|(_, s)| s), - allow_internal_unstable: allow_internal_unstable, - }, - }); - + NormalTT(ref expandfun, def_info, allow_internal_unstable) => { + if let Err(msg) = validate_and_set_expn_info(def_info.map(|(_, s)| s), + allow_internal_unstable) { + self.cx.span_err(path.span, &msg); + return kind.dummy(span); + } kind.make_from(expandfun.expand(self.cx, span, marked_tts)) } @@ -687,7 +699,7 @@ macro_rules! fully_configure { impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect(&mut self, expansion_kind: ExpansionKind, kind: InvocationKind) -> Expansion { - let mark = Mark::fresh(); + let mark = Mark::fresh(self.cx.current_expansion.mark); self.invocations.push(Invocation { kind: kind, expansion_kind: expansion_kind, diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 73494d47fee9b..a9252d0818e38 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -162,6 +162,12 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) let lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs")); let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs")); + // Parse the macro_rules! invocation + let body = match def.node { + ast::ItemKind::MacroDef(ref body) => body, + _ => unreachable!(), + }; + // The pattern that macro_rules matches. // The grammar for macro_rules! is: // $( $lhs:tt => $rhs:tt );+ @@ -174,7 +180,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) quoted::TokenTree::Token(DUMMY_SP, token::FatArrow), quoted::TokenTree::MetaVarDecl(DUMMY_SP, rhs_nm, ast::Ident::from_str("tt")), ], - separator: Some(token::Semi), + separator: Some(if body.legacy { token::Semi } else { token::Comma }), op: quoted::KleeneOp::OneOrMore, num_captures: 2, })), @@ -187,12 +193,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) })), ]; - // Parse the macro_rules! invocation - let body = match def.node { - ast::ItemKind::MacroDef(ref body) => body.clone().into(), - _ => unreachable!(), - }; - let argument_map = match parse(sess, body, &argument_gram, None, true) { + let argument_map = match parse(sess, body.stream(), &argument_gram, None, true) { Success(m) => m, Failure(sp, tok) => { let s = parse_failure_msg(tok); @@ -252,9 +253,12 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) valid: valid, }); - NormalTT(exp, - Some((def.id, def.span)), - attr::contains_name(&def.attrs, "allow_internal_unstable")) + if body.legacy { + let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable"); + NormalTT(exp, Some((def.id, def.span)), allow_internal_unstable) + } else { + SyntaxExtension::DeclMacro(exp, Some(def.span)) + } } fn check_lhs_nt_follows(sess: &ParseSess, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 6acf27f314ab5..6ea8fab98aa2f 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -306,9 +306,12 @@ declare_features! ( // The `unadjusted` ABI. Perma unstable. (active, abi_unadjusted, "1.16.0", None), - // Macros 1.1 + // Procedural macros 2.0. (active, proc_macro, "1.16.0", Some(38356)), + // Declarative macros 2.0 (`macro`). + (active, decl_macro, "1.17.0", Some(39412)), + // Allows attributes on struct literal fields. (active, struct_field_attributes, "1.16.0", Some(38814)), @@ -1225,6 +1228,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } + ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => { + let msg = "`macro` is experimental"; + gate_feature_post!(&self, decl_macro, i.span, msg); + } + _ => {} } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 58cf50cdc000c..4c6cf49a8db43 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -189,6 +189,10 @@ pub trait Folder : Sized { // fold::noop_fold_mac(_mac, self) } + fn fold_macro_def(&mut self, def: MacroDef) -> MacroDef { + noop_fold_macro_def(def, self) + } + fn fold_lifetime(&mut self, l: Lifetime) -> Lifetime { noop_fold_lifetime(l, self) } @@ -424,7 +428,7 @@ pub fn noop_fold_global_asm(ga: P, pub fn noop_fold_variant(v: Variant, fld: &mut T) -> Variant { Spanned { node: Variant_ { - name: v.node.name, + name: fld.fold_ident(v.node.name), attrs: fold_attrs(v.node.attrs, fld), data: fld.fold_variant_data(v.node.data), disr_expr: v.node.disr_expr.map(|e| fld.fold_expr(e)), @@ -515,6 +519,13 @@ pub fn noop_fold_mac(Spanned {node, span}: Mac, fld: &mut T) -> Mac { } } +pub fn noop_fold_macro_def(def: MacroDef, fld: &mut T) -> MacroDef { + MacroDef { + tokens: fld.fold_tts(def.tokens.into()).into(), + legacy: def.legacy, + } +} + pub fn noop_fold_meta_list_item(li: NestedMetaItem, fld: &mut T) -> NestedMetaItem { Spanned { @@ -683,7 +694,7 @@ pub fn noop_fold_ty_params(tps: Vec, fld: &mut T) -> Vec(l: Lifetime, fld: &mut T) -> Lifetime { Lifetime { id: fld.new_id(l.id), - name: l.name, + ident: fld.fold_ident(l.ident), span: fld.new_span(l.span) } } @@ -919,7 +930,7 @@ pub fn noop_fold_item_kind(i: ItemKind, folder: &mut T) -> ItemKind { items.move_flat_map(|item| folder.fold_trait_item(item)), ), ItemKind::Mac(m) => ItemKind::Mac(folder.fold_mac(m)), - ItemKind::MacroDef(tts) => ItemKind::MacroDef(folder.fold_tts(tts.into()).into()), + ItemKind::MacroDef(def) => ItemKind::MacroDef(folder.fold_macro_def(def)), } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c28f678cb5197..8d7c8c5248bb9 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1958,7 +1958,7 @@ impl<'a> Parser<'a> { token::Lifetime(ident) => { let ident_span = self.span; self.bump(); - Lifetime { name: ident.name, span: ident_span, id: ast::DUMMY_NODE_ID } + Lifetime { ident: ident, span: ident_span, id: ast::DUMMY_NODE_ID } } _ => self.span_bug(self.span, "not a lifetime") } @@ -3758,31 +3758,59 @@ impl<'a> Parser<'a> { fn eat_macro_def(&mut self, attrs: &[Attribute], vis: &Visibility) -> PResult<'a, Option>> { let lo = self.span; - match self.token { - token::Ident(ident) if ident.name == "macro_rules" => { - if self.look_ahead(1, |t| *t == token::Not) { - let prev_span = self.prev_span; - self.complain_if_pub_macro(vis, prev_span); - self.bump(); - self.bump(); + let (ident, def) = match self.token { + token::Ident(ident) if ident.name == keywords::Macro.name() => { + self.bump(); + let ident = self.parse_ident()?; + let tokens = if self.check(&token::OpenDelim(token::Brace)) { + match self.parse_token_tree() { + TokenTree::Delimited(_, ref delimited) => delimited.stream(), + _ => unreachable!(), + } + } else if self.check(&token::OpenDelim(token::Paren)) { + let args = self.parse_token_tree(); + let body = if self.check(&token::OpenDelim(token::Brace)) { + self.parse_token_tree() + } else { + self.unexpected()?; + unreachable!() + }; + TokenStream::concat(vec![ + args.into(), + TokenTree::Token(lo.to(self.prev_span), token::FatArrow).into(), + body.into(), + ]) + } else { + self.unexpected()?; + unreachable!() + }; + + (ident, ast::MacroDef { tokens: tokens.into(), legacy: false }) + } + token::Ident(ident) if ident.name == "macro_rules" && + self.look_ahead(1, |t| *t == token::Not) => { + let prev_span = self.prev_span; + self.complain_if_pub_macro(vis, prev_span); + self.bump(); + self.bump(); + + let ident = self.parse_ident()?; + let (delim, tokens) = self.expect_delimited_token_tree()?; + if delim != token::Brace { + if !self.eat(&token::Semi) { + let msg = "macros that expand to items must either \ + be surrounded with braces or followed by a semicolon"; + self.span_err(self.prev_span, msg); + } } + + (ident, ast::MacroDef { tokens: tokens, legacy: true }) } _ => return Ok(None), }; - let id = self.parse_ident()?; - let (delim, tts) = self.expect_delimited_token_tree()?; - if delim != token::Brace { - if !self.eat(&token::Semi) { - let msg = "macros that expand to items must either be surrounded with braces \ - or followed by a semicolon"; - self.span_err(self.prev_span, msg); - } - } - let span = lo.to(self.prev_span); - let kind = ItemKind::MacroDef(tts); - Ok(Some(self.mk_item(span, id, kind, Visibility::Inherited, attrs.to_owned()))) + Ok(Some(self.mk_item(span, ident, ItemKind::MacroDef(def), vis.clone(), attrs.to_vec()))) } fn parse_stmt_without_recovery(&mut self, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 83c289ff80b92..073ededcb0ce7 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1392,7 +1392,7 @@ impl<'a> State<'a> { self.print_ident(item.ident)?; self.cbox(INDENT_UNIT)?; self.popen()?; - self.print_tts(tts.clone().into())?; + self.print_tts(tts.stream())?; self.pclose()?; word(&mut self.s, ";")?; self.end()?; @@ -2764,7 +2764,7 @@ impl<'a> State<'a> { lifetime: &ast::Lifetime) -> io::Result<()> { - self.print_name(lifetime.name) + self.print_name(lifetime.ident.name) } pub fn print_lifetime_bounds(&mut self, diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 8e257102e1c13..a8a9ae556f108 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -21,7 +21,7 @@ use tokenstream::TokenStream; /// call to codemap's `is_internal` check. /// The expanded code uses the unstable `#[prelude_import]` attribute. fn ignored_span(sp: Span) -> Span { - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); mark.set_expn_info(ExpnInfo { call_site: DUMMY_SP, callee: NameAndSpan { diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index bb1a6ff65a596..837c3eb0100c6 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -276,7 +276,7 @@ fn generate_test_harness(sess: &ParseSess, let mut cleaner = EntryPointCleaner { depth: 0 }; let krate = cleaner.fold_crate(krate); - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); let mut cx: TestCtxt = TestCtxt { sess: sess, span_diagnostic: sd, @@ -591,7 +591,7 @@ fn mk_tests(cx: &TestCtxt) -> P { let struct_type = ecx.ty_path(ecx.path(sp, vec![ecx.ident_of("self"), ecx.ident_of("test"), ecx.ident_of("TestDescAndFn")])); - let static_lt = ecx.lifetime(sp, keywords::StaticLifetime.name()); + let static_lt = ecx.lifetime(sp, keywords::StaticLifetime.ident()); // &'static [self::test::TestDescAndFn] let static_type = ecx.ty_rptr(sp, ecx.ty(sp, ast::TyKind::Slice(struct_type)), diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 0fa0753b22c82..18a0949af0eb1 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -27,7 +27,6 @@ use abi::Abi; use ast::*; use syntax_pos::Span; use codemap::Spanned; -use tokenstream::ThinTokenStream; #[derive(Copy, Clone, PartialEq, Eq)] pub enum FnKind<'a> { @@ -113,7 +112,7 @@ pub trait Visitor<'ast>: Sized { // definition in your trait impl: // visit::walk_mac(self, _mac) } - fn visit_mac_def(&mut self, _mac: &'ast ThinTokenStream, _id: NodeId) { + fn visit_mac_def(&mut self, _mac: &'ast MacroDef, _id: NodeId) { // Nothing to do } fn visit_path(&mut self, path: &'ast Path, _id: NodeId) { @@ -196,7 +195,7 @@ pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) { } pub fn walk_lifetime<'a, V: Visitor<'a>>(visitor: &mut V, lifetime: &'a Lifetime) { - visitor.visit_name(lifetime.span, lifetime.name); + visitor.visit_ident(lifetime.span, lifetime.ident); } pub fn walk_lifetime_def<'a, V: Visitor<'a>>(visitor: &mut V, lifetime_def: &'a LifetimeDef) { diff --git a/src/libsyntax_ext/deriving/generic/ty.rs b/src/libsyntax_ext/deriving/generic/ty.rs index cfd52381538cb..9c89f99cbb5b3 100644 --- a/src/libsyntax_ext/deriving/generic/ty.rs +++ b/src/libsyntax_ext/deriving/generic/ty.rs @@ -118,14 +118,14 @@ pub fn nil_ty<'r>() -> Ty<'r> { fn mk_lifetime(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Option { match *lt { - Some(ref s) => Some(cx.lifetime(span, cx.ident_of(*s).name)), + Some(s) => Some(cx.lifetime(span, Ident::from_str(s))), None => None, } } fn mk_lifetimes(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Vec { match *lt { - Some(ref s) => vec![cx.lifetime(span, cx.ident_of(*s).name)], + Some(s) => vec![cx.lifetime(span, Ident::from_str(s))], None => vec![], } } @@ -243,11 +243,11 @@ impl<'a> LifetimeBounds<'a> { -> Generics { let lifetimes = self.lifetimes .iter() - .map(|&(ref lt, ref bounds)| { + .map(|&(lt, ref bounds)| { let bounds = bounds.iter() - .map(|b| cx.lifetime(span, cx.ident_of(*b).name)) + .map(|b| cx.lifetime(span, Ident::from_str(b))) .collect(); - cx.lifetime_def(span, cx.ident_of(*lt).name, vec![], bounds) + cx.lifetime_def(span, Ident::from_str(lt), vec![], bounds) }) .collect(); let ty_params = self.bounds @@ -277,7 +277,7 @@ pub fn get_explicit_self(cx: &ExtCtxt, respan(span, match *ptr { Borrowed(ref lt, mutbl) => { - let lt = lt.map(|s| cx.lifetime(span, cx.ident_of(s).name)); + let lt = lt.map(|s| cx.lifetime(span, Ident::from_str(s))); SelfKind::Region(lt, mutbl) } Raw(_) => { diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 31c7cc3367677..00dcfc7a58706 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -162,7 +162,7 @@ fn call_intrinsic(cx: &ExtCtxt, } else { // Avoid instability errors with user defined curstom derives, cc #36316 let mut info = cx.current_expansion.mark.expn_info().unwrap(); info.callee.allow_internal_unstable = true; - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); mark.set_expn_info(info); span.ctxt = SyntaxContext::empty().apply_mark(mark); } diff --git a/src/libsyntax_ext/env.rs b/src/libsyntax_ext/env.rs index ecf0a8f377ea3..affebbabbbda4 100644 --- a/src/libsyntax_ext/env.rs +++ b/src/libsyntax_ext/env.rs @@ -13,7 +13,7 @@ // interface. // -use syntax::ast; +use syntax::ast::{self, Ident}; use syntax::ext::base::*; use syntax::ext::base; use syntax::ext::build::AstBuilder; @@ -39,10 +39,9 @@ pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, cx.std_path(&["option", "Option", "None"]), Vec::new(), vec![cx.ty_rptr(sp, - cx.ty_ident(sp, cx.ident_of("str")), + cx.ty_ident(sp, Ident::from_str("str")), Some(cx.lifetime(sp, - cx.ident_of("'static") - .name)), + Ident::from_str("'static"))), ast::Mutability::Immutable)], Vec::new())) } diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 6f5ab50b2fe6f..24c1dfe289b1e 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -508,7 +508,7 @@ impl<'a, 'b> Context<'a, 'b> { let sp = piece_ty.span; let ty = ecx.ty_rptr(sp, ecx.ty(sp, ast::TyKind::Slice(piece_ty)), - Some(ecx.lifetime(sp, keywords::StaticLifetime.name())), + Some(ecx.lifetime(sp, keywords::StaticLifetime.ident())), ast::Mutability::Immutable); let slice = ecx.expr_vec_slice(sp, pieces); // static instead of const to speed up codegen by not requiring this to be inlined @@ -536,7 +536,7 @@ impl<'a, 'b> Context<'a, 'b> { // First, build up the static array which will become our precompiled // format "string" - let static_lifetime = self.ecx.lifetime(self.fmtsp, keywords::StaticLifetime.name()); + let static_lifetime = self.ecx.lifetime(self.fmtsp, keywords::StaticLifetime.ident()); let piece_ty = self.ecx.ty_rptr(self.fmtsp, self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")), Some(static_lifetime), diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index 6318abec69f06..ab6d73e5061a0 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -361,7 +361,7 @@ fn mk_registrar(cx: &mut ExtCtxt, custom_derives: &[ProcMacroDerive], custom_attrs: &[ProcMacroDef], custom_macros: &[ProcMacroDef]) -> P { - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); mark.set_expn_info(ExpnInfo { call_site: DUMMY_SP, callee: NameAndSpan { diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 8a9ff647b3ea1..f2ccc3f051e92 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -16,7 +16,7 @@ //! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093 use Span; -use symbol::Symbol; +use symbol::{Ident, Symbol}; use serialize::{Encodable, Decodable, Encoder, Decoder}; use std::cell::RefCell; @@ -24,23 +24,31 @@ use std::collections::HashMap; use std::fmt; /// A SyntaxContext represents a chain of macro expansions (represented by marks). -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] pub struct SyntaxContext(u32); -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct SyntaxContextData { pub outer_mark: Mark, pub prev_ctxt: SyntaxContext, + pub modern: SyntaxContext, } /// A mark is a unique id associated with a macro expansion. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, RustcEncodable, RustcDecodable)] pub struct Mark(u32); +#[derive(Default)] +struct MarkData { + parent: Mark, + modern: bool, + expn_info: Option, +} + impl Mark { - pub fn fresh() -> Self { + pub fn fresh(parent: Mark) -> Self { HygieneData::with(|data| { - data.marks.push(None); + data.marks.push(MarkData { parent: parent, modern: false, expn_info: None }); Mark(data.marks.len() as u32 - 1) }) } @@ -59,29 +67,59 @@ impl Mark { } pub fn expn_info(self) -> Option { - HygieneData::with(|data| data.marks[self.0 as usize].clone()) + HygieneData::with(|data| data.marks[self.0 as usize].expn_info.clone()) } pub fn set_expn_info(self, info: ExpnInfo) { - HygieneData::with(|data| data.marks[self.0 as usize] = Some(info)) + HygieneData::with(|data| data.marks[self.0 as usize].expn_info = Some(info)) + } + + pub fn modern(mut self) -> Mark { + HygieneData::with(|data| { + loop { + if self == Mark::root() || data.marks[self.0 as usize].modern { + return self; + } + self = data.marks[self.0 as usize].parent; + } + }) + } + + pub fn is_modern(self) -> bool { + HygieneData::with(|data| data.marks[self.0 as usize].modern) + } + + pub fn set_modern(self) { + HygieneData::with(|data| data.marks[self.0 as usize].modern = true) + } + + pub fn is_descendant_of(mut self, ancestor: Mark) -> bool { + HygieneData::with(|data| { + while self != ancestor { + if self == Mark::root() { + return false; + } + self = data.marks[self.0 as usize].parent; + } + true + }) } } struct HygieneData { - marks: Vec>, + marks: Vec, syntax_contexts: Vec, markings: HashMap<(SyntaxContext, Mark), SyntaxContext>, + gensym_to_ctxt: HashMap, } impl HygieneData { fn new() -> Self { HygieneData { - marks: vec![None], - syntax_contexts: vec![SyntaxContextData { - outer_mark: Mark::root(), - prev_ctxt: SyntaxContext::empty(), - }], + marks: vec![MarkData::default()], + syntax_contexts: vec![SyntaxContextData::default()], markings: HashMap::new(), + gensym_to_ctxt: HashMap::new(), } } @@ -102,30 +140,146 @@ impl SyntaxContext { SyntaxContext(0) } - pub fn data(self) -> SyntaxContextData { - HygieneData::with(|data| data.syntax_contexts[self.0 as usize]) - } - /// Extend a syntax context with a given mark pub fn apply_mark(self, mark: Mark) -> SyntaxContext { - // Applying the same mark twice is a no-op - let ctxt_data = self.data(); - if mark == ctxt_data.outer_mark { - return ctxt_data.prev_ctxt; - } - HygieneData::with(|data| { let syntax_contexts = &mut data.syntax_contexts; + let ctxt_data = syntax_contexts[self.0 as usize]; + if mark == ctxt_data.outer_mark { + return ctxt_data.prev_ctxt; + } + + let modern = if data.marks[mark.0 as usize].modern { + *data.markings.entry((ctxt_data.modern, mark)).or_insert_with(|| { + let modern = SyntaxContext(syntax_contexts.len() as u32); + syntax_contexts.push(SyntaxContextData { + outer_mark: mark, + prev_ctxt: ctxt_data.modern, + modern: modern, + }); + modern + }) + } else { + ctxt_data.modern + }; + *data.markings.entry((self, mark)).or_insert_with(|| { syntax_contexts.push(SyntaxContextData { outer_mark: mark, prev_ctxt: self, + modern: modern, }); SyntaxContext(syntax_contexts.len() as u32 - 1) }) }) } + pub fn remove_mark(&mut self) -> Mark { + HygieneData::with(|data| { + let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark; + *self = data.syntax_contexts[self.0 as usize].prev_ctxt; + outer_mark + }) + } + + /// Adjust this context for resolution in a scope created by the given expansion. + /// For example, consider the following three resolutions of `f`: + /// ```rust + /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. + /// m!(f); + /// macro m($f:ident) { + /// mod bar { + /// pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`. + /// pub fn $f() {} // `$f`'s `SyntaxContext` is empty. + /// } + /// foo::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m` + /// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`, + /// //| and it resolves to `::foo::f`. + /// bar::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m` + /// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`, + /// //| and it resolves to `::bar::f`. + /// bar::$f(); // `f`'s `SyntaxContext` is empty. + /// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`, + /// //| and it resolves to `::bar::$f`. + /// } + /// ``` + /// This returns the expansion whose definition scope we use to privacy check the resolution, + /// or `None` if we privacy check as usual (i.e. not w.r.t. a macro definition scope). + pub fn adjust(&mut self, expansion: Mark) -> Option { + let mut scope = None; + while !expansion.is_descendant_of(self.outer()) { + scope = Some(self.remove_mark()); + } + scope + } + + /// Adjust this context for resolution in a scope created by the given expansion + /// via a glob import with the given `SyntaxContext`. + /// For example, + /// ```rust + /// m!(f); + /// macro m($i:ident) { + /// mod foo { + /// pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`. + /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty. + /// } + /// n(f); + /// macro n($j:ident) { + /// use foo::*; + /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n` + /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`. + /// $i(); // `$i`'s `SyntaxContext` has a mark from `n` + /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`. + /// $j(); // `$j`'s `SyntaxContext` has a mark from `m` + /// //^ This cannot be glob-adjusted, so this is a resolution error. + /// } + /// } + /// ``` + /// This returns `None` if the context cannot be glob-adjusted. + /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details). + pub fn glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext) + -> Option> { + let mut scope = None; + while !expansion.is_descendant_of(glob_ctxt.outer()) { + scope = Some(glob_ctxt.remove_mark()); + if self.remove_mark() != scope.unwrap() { + return None; + } + } + if self.adjust(expansion).is_some() { + return None; + } + Some(scope) + } + + /// Undo `glob_adjust` if possible: + /// ```rust + /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) { + /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); + /// } + /// ``` + pub fn reverse_glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext) + -> Option> { + if self.adjust(expansion).is_some() { + return None; + } + + let mut marks = Vec::new(); + while !expansion.is_descendant_of(glob_ctxt.outer()) { + marks.push(glob_ctxt.remove_mark()); + } + + let scope = marks.last().cloned(); + while let Some(mark) = marks.pop() { + *self = self.apply_mark(mark); + } + Some(scope) + } + + pub fn modern(self) -> SyntaxContext { + HygieneData::with(|data| data.syntax_contexts[self.0 as usize].modern) + } + pub fn outer(self) -> Mark { HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark) } @@ -200,3 +354,22 @@ impl Decodable for SyntaxContext { Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene } } + +impl Symbol { + pub fn from_ident(ident: Ident) -> Symbol { + HygieneData::with(|data| { + let gensym = ident.name.gensymed(); + data.gensym_to_ctxt.insert(gensym, ident.ctxt); + gensym + }) + } + + pub fn to_ident(self) -> Ident { + HygieneData::with(|data| { + match data.gensym_to_ctxt.get(&self) { + Some(&ctxt) => Ident { name: self.interned(), ctxt: ctxt }, + None => Ident::with_empty_ctxt(self), + } + }) + } +} diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index b866652c49f85..73c0256f2c1f5 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -35,8 +35,8 @@ impl Ident { Ident::with_empty_ctxt(Symbol::intern(string)) } - pub fn unhygienize(self) -> Ident { - Ident { name: self.name, ctxt: SyntaxContext::empty() } + pub fn modern(self) -> Ident { + Ident { name: self.name, ctxt: self.ctxt.modern() } } } @@ -54,13 +54,24 @@ impl fmt::Display for Ident { impl Encodable for Ident { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - self.name.encode(s) + if self.ctxt.modern() == SyntaxContext::empty() { + s.emit_str(&self.name.as_str()) + } else { // FIXME(jseyfried) intercrate hygiene + let mut string = "#".to_owned(); + string.push_str(&self.name.as_str()); + s.emit_str(&string) + } } } impl Decodable for Ident { fn decode(d: &mut D) -> Result { - Ok(Ident::with_empty_ctxt(Symbol::decode(d)?)) + let string = d.read_str()?; + Ok(if !string.starts_with('#') { + Ident::from_str(&string) + } else { // FIXME(jseyfried) intercrate hygiene + Ident::with_empty_ctxt(Symbol::gensym(&string[1..])) + }) } } @@ -77,11 +88,19 @@ impl Symbol { with_interner(|interner| interner.intern(string)) } + pub fn interned(self) -> Self { + with_interner(|interner| interner.interned(self)) + } + /// gensym's a new usize, using the current interner. pub fn gensym(string: &str) -> Self { with_interner(|interner| interner.gensym(string)) } + pub fn gensymed(self) -> Self { + with_interner(|interner| interner.gensymed(self)) + } + pub fn as_str(self) -> InternedString { with_interner(|interner| unsafe { InternedString { @@ -129,6 +148,7 @@ impl> PartialEq for Symbol { pub struct Interner { names: HashMap, Symbol>, strings: Vec>, + gensyms: Vec, } impl Interner { @@ -156,15 +176,29 @@ impl Interner { name } + pub fn interned(&self, symbol: Symbol) -> Symbol { + if (symbol.0 as usize) < self.strings.len() { + symbol + } else { + self.interned(self.gensyms[(!0 - symbol.0) as usize]) + } + } + fn gensym(&mut self, string: &str) -> Symbol { - let gensym = Symbol(self.strings.len() as u32); - // leave out of `names` to avoid colliding - self.strings.push(string.to_string().into_boxed_str()); - gensym + let symbol = self.intern(string); + self.gensymed(symbol) } - pub fn get(&self, name: Symbol) -> &str { - &self.strings[name.0 as usize] + fn gensymed(&mut self, symbol: Symbol) -> Symbol { + self.gensyms.push(symbol); + Symbol(!0 - self.gensyms.len() as u32 + 1) + } + + pub fn get(&self, symbol: Symbol) -> &str { + match self.strings.get(symbol.0 as usize) { + Some(ref string) => string, + None => self.get(self.gensyms[(!0 - symbol.0) as usize]), + } } } @@ -379,11 +413,10 @@ mod tests { assert_eq!(i.intern("cat"), Symbol(1)); // dog is still at zero assert_eq!(i.intern("dog"), Symbol(0)); - // gensym gets 3 - assert_eq!(i.gensym("zebra"), Symbol(2)); + assert_eq!(i.gensym("zebra"), Symbol(4294967295)); // gensym of same string gets new number : - assert_eq!(i.gensym("zebra"), Symbol(3)); + assert_eq!(i.gensym("zebra"), Symbol(4294967294)); // gensym of *existing* string gets new number: - assert_eq!(i.gensym("dog"), Symbol(4)); + assert_eq!(i.gensym("dog"), Symbol(4294967293)); } } diff --git a/src/test/compile-fail/feature-gate-decl_macro.rs b/src/test/compile-fail/feature-gate-decl_macro.rs new file mode 100644 index 0000000000000..af7d5fec07199 --- /dev/null +++ b/src/test/compile-fail/feature-gate-decl_macro.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro m() {} //~ ERROR `macro` is experimental (see issue #39412) +//~| HELP add #![feature(decl_macro)] to the crate attributes to enable + +fn main() {} diff --git a/src/test/compile-fail/hygiene/globs.rs b/src/test/compile-fail/hygiene/globs.rs new file mode 100644 index 0000000000000..7ba217061c66e --- /dev/null +++ b/src/test/compile-fail/hygiene/globs.rs @@ -0,0 +1,68 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(decl_macro)] + +mod foo { + pub fn f() {} +} + +mod bar { + pub fn g() {} +} + +macro m($($t:tt)*) { + $($t)* + use foo::*; + f(); + g(); //~ ERROR cannot find function `g` in this scope +} + +fn main() { + m! { + use bar::*; + g(); + f(); //~ ERROR cannot find function `f` in this scope + } +} + +n!(f); +macro n($i:ident) { + mod foo { + pub fn $i() -> u32 { 0 } + pub fn f() {} + + mod test { + use super::*; + fn g() { + let _: u32 = $i(); + let _: () = f(); + } + } + + macro n($j:ident) { + mod test { + use super::*; + fn g() { + let _: u32 = $i(); + let _: () = f(); + $j(); + } + } + } + + n!(f); + mod test2 { + super::n! { + f //~ ERROR cannot find function `f` in this scope + } + } + } +} diff --git a/src/test/compile-fail/hygiene/nested_macro_privacy.rs b/src/test/compile-fail/hygiene/nested_macro_privacy.rs new file mode 100644 index 0000000000000..6612359649c19 --- /dev/null +++ b/src/test/compile-fail/hygiene/nested_macro_privacy.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(decl_macro)] + +macro n($foo:ident, $S:ident, $i:ident, $m:ident) { + mod $foo { + #[derive(Default)] + pub struct $S { $i: u32 } + pub macro $m($e:expr) { $e.$i } + } +} + +n!(foo, S, i, m); + +fn main() { + use foo::{S, m}; + S::default().i; //~ ERROR field `i` of struct `foo::S` is private + m!(S::default()); // ok +} diff --git a/src/test/compile-fail/hygiene/no_implicit_prelude.rs b/src/test/compile-fail/hygiene/no_implicit_prelude.rs new file mode 100644 index 0000000000000..c90c7b3093c9f --- /dev/null +++ b/src/test/compile-fail/hygiene/no_implicit_prelude.rs @@ -0,0 +1,25 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(decl_macro)] + +mod foo { + pub macro m() { Vec::new(); ().clone() } + fn f() { ::bar::m!(); } +} + +#[no_implicit_prelude] +mod bar { + pub macro m() { + Vec::new(); //~ ERROR failed to resolve + ().clone() //~ ERROR no method named `clone` found + } + fn f() { ::foo::m!(); } +} diff --git a/src/test/compile-fail/hygiene/privacy.rs b/src/test/compile-fail/hygiene/privacy.rs new file mode 100644 index 0000000000000..987cad187d428 --- /dev/null +++ b/src/test/compile-fail/hygiene/privacy.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(decl_macro)] + +mod foo { + fn f() {} + + pub macro m($e:expr) { + f(); + self::f(); + ::foo::f(); + $e + } +} + +fn main() { + foo::m!( + foo::f() //~ ERROR `f` is private + ); +} diff --git a/src/test/compile-fail/hygiene/trait_items.rs b/src/test/compile-fail/hygiene/trait_items.rs new file mode 100644 index 0000000000000..3bd19cbc0ac67 --- /dev/null +++ b/src/test/compile-fail/hygiene/trait_items.rs @@ -0,0 +1,31 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(decl_macro)] + +mod foo { + pub trait T { + fn f(&self) {} + } + impl T for () {} +} + +mod bar { + use foo::*; + pub macro m() { ().f() } + fn f() { ::baz::m!(); } +} + +mod baz { + pub macro m() { ().f() } //~ ERROR no method named `f` found for type `()` in the current scope + fn f() { ::bar::m!(); } +} + +fn main() {} diff --git a/src/test/run-pass/hygiene/arguments.rs b/src/test/run-pass/hygiene/arguments.rs new file mode 100644 index 0000000000000..5d9e1863847da --- /dev/null +++ b/src/test/run-pass/hygiene/arguments.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] + +macro m($t:ty, $e:expr) { + mod foo { + #[allow(unused)] + struct S; + pub(super) fn f(_: $t) {} + } + foo::f($e); +} + +fn main() { + struct S; + m!(S, S); +} diff --git a/src/test/run-pass/hygiene/auxiliary/intercrate.rs b/src/test/run-pass/hygiene/auxiliary/intercrate.rs new file mode 100644 index 0000000000000..aa67e5c5f4db7 --- /dev/null +++ b/src/test/run-pass/hygiene/auxiliary/intercrate.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(decl_macro)] + +pub mod foo { + pub use self::bar::m; + mod bar { + fn f() -> u32 { 1 } + pub macro m() { + f(); + } + } +} diff --git a/src/test/run-pass/hygiene/fields.rs b/src/test/run-pass/hygiene/fields.rs new file mode 100644 index 0000000000000..d7f99ba2118e8 --- /dev/null +++ b/src/test/run-pass/hygiene/fields.rs @@ -0,0 +1,40 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] + +mod foo { + struct S { x: u32 } + struct T(u32); + + pub macro m($S:ident, $x:ident) {{ + struct $S { + $x: u32, + x: i32, + } + + let s = S { x: 0 }; + let _ = s.x; + + let t = T(0); + let _ = t.0; + + let s = $S { $x: 0, x: 1 }; + assert_eq!((s.$x, s.x), (0, 1)); + s + }} +} + +fn main() { + let s = foo::m!(S, x); + assert_eq!(s.x, 0); +} diff --git a/src/test/run-pass/hygiene/impl_items.rs b/src/test/run-pass/hygiene/impl_items.rs new file mode 100644 index 0000000000000..a5780a573aba1 --- /dev/null +++ b/src/test/run-pass/hygiene/impl_items.rs @@ -0,0 +1,44 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] + +mod foo { + struct S; + impl S { + fn f(&self) {} + } + + pub macro m() { + let _: () = S.f(); + } +} + +struct S; + +macro m($f:ident) { + impl S { + fn f(&self) -> u32 { 0 } + fn $f(&self) -> i32 { 0 } + } + fn f() { + let _: u32 = S.f(); + let _: i32 = S.$f(); + } +} + +m!(f); + +fn main() { + let _: i32 = S.f(); + foo::m!(); +} diff --git a/src/test/run-pass/hygiene/intercrate.rs b/src/test/run-pass/hygiene/intercrate.rs new file mode 100644 index 0000000000000..3a75085f22e4e --- /dev/null +++ b/src/test/run-pass/hygiene/intercrate.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +// aux-build:intercrate.rs + +#![feature(decl_macro)] + +extern crate intercrate; + +fn main() { + assert_eq!(intercrate::foo::m!(), 1); +} diff --git a/src/test/run-pass/hygiene/items.rs b/src/test/run-pass/hygiene/items.rs new file mode 100644 index 0000000000000..121c341a124ce --- /dev/null +++ b/src/test/run-pass/hygiene/items.rs @@ -0,0 +1,36 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] + +pub macro m($foo:ident, $f:ident, $e:expr) { + mod foo { + pub fn f() -> u32 { 0 } + pub fn $f() -> u64 { 0 } + } + + mod $foo { + pub fn f() -> i32 { 0 } + pub fn $f() -> i64 { 0 } + } + + let _: u32 = foo::f(); + let _: u64 = foo::$f(); + let _: i32 = $foo::f(); + let _: i64 = $foo::$f(); + let _: i64 = $e; +} + +fn main() { + m!(foo, f, foo::f()); + let _: i64 = foo::f(); +} diff --git a/src/test/run-pass/hygiene/lexical.rs b/src/test/run-pass/hygiene/lexical.rs new file mode 100644 index 0000000000000..cb02a17fec38a --- /dev/null +++ b/src/test/run-pass/hygiene/lexical.rs @@ -0,0 +1,33 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] + +mod bar { + mod baz { + pub fn f() {} + } + + pub macro m($f:ident) { + baz::f(); + let _: i32 = $f(); + { + fn $f() -> u32 { 0 } + let _: u32 = $f(); + } + } +} + +fn main() { + fn f() -> i32 { 0 } + bar::m!(f); +} diff --git a/src/test/run-pass/hygiene/trait_items.rs b/src/test/run-pass/hygiene/trait_items.rs new file mode 100644 index 0000000000000..2344c97ce1b1c --- /dev/null +++ b/src/test/run-pass/hygiene/trait_items.rs @@ -0,0 +1,29 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] + +macro m($T:ident, $f:ident) { + pub trait $T { + fn f(&self) -> u32 { 0 } + fn $f(&self) -> i32 { 0 } + } + impl $T for () {} + + let _: u32 = ().f(); + let _: i32 = ().$f(); +} + +fn main() { + m!(T, f); + let _: i32 = ().f(); +} diff --git a/src/test/run-pass/hygiene/ty_params.rs b/src/test/run-pass/hygiene/ty_params.rs new file mode 100644 index 0000000000000..510528ede40c5 --- /dev/null +++ b/src/test/run-pass/hygiene/ty_params.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro)] + +macro m($T:ident) { + fn f(t: T, t2: $T) -> (T, $T) { + (t, t2) + } +} + +m!(T); + +fn main() {}