about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libproc_macro/lib.rs18
-rw-r--r--src/librustc/hir/lowering.rs58
-rw-r--r--src/librustc/hir/map/def_collector.rs6
-rw-r--r--src/librustc/ich/impls_hir.rs5
-rw-r--r--src/librustc/ich/impls_syntax.rs6
-rw-r--r--src/librustc/lint/context.rs4
-rw-r--r--src/librustc/lint/levels.rs2
-rw-r--r--src/librustc/lint/mod.rs2
-rw-r--r--src/librustc/session/config.rs4
-rw-r--r--src/librustc/traits/on_unimplemented.rs2
-rw-r--r--src/librustc/ty/mod.rs6
-rw-r--r--src/librustc_driver/lib.rs2
-rw-r--r--src/librustc_driver/pretty.rs4
-rw-r--r--src/librustc_incremental/assert_dep_graph.rs2
-rw-r--r--src/librustc_lint/builtin.rs10
-rw-r--r--src/librustc_lint/unused.rs2
-rw-r--r--src/librustc_mir/build/mod.rs4
-rw-r--r--src/librustc_mir/dataflow/mod.rs2
-rw-r--r--src/librustc_mir/hair/pattern/mod.rs6
-rw-r--r--src/librustc_passes/ast_validation.rs35
-rw-r--r--src/librustc_privacy/lib.rs13
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs27
-rw-r--r--src/librustc_resolve/lib.rs200
-rw-r--r--src/librustc_resolve/macros.rs37
-rw-r--r--src/librustc_resolve/resolve_imports.rs36
-rw-r--r--src/librustc_save_analysis/dump_visitor.rs42
-rw-r--r--src/librustc_save_analysis/lib.rs20
-rw-r--r--src/librustc_save_analysis/sig.rs4
-rw-r--r--src/librustc_typeck/check/mod.rs6
-rw-r--r--src/librustdoc/clean/auto_trait.rs2
-rw-r--r--src/librustdoc/clean/cfg.rs72
-rw-r--r--src/librustdoc/clean/mod.rs21
-rw-r--r--src/librustdoc/html/render.rs2
-rw-r--r--src/libsyntax/ast.rs56
-rw-r--r--src/libsyntax/attr.rs106
-rw-r--r--src/libsyntax/config.rs2
-rw-r--r--src/libsyntax/ext/base.rs8
-rw-r--r--src/libsyntax/ext/build.rs54
-rw-r--r--src/libsyntax/ext/derive.rs2
-rw-r--r--src/libsyntax/ext/expand.rs29
-rw-r--r--src/libsyntax/ext/quote.rs6
-rw-r--r--src/libsyntax/ext/tt/macro_parser.rs8
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs2
-rw-r--r--src/libsyntax/ext/tt/quoted.rs11
-rw-r--r--src/libsyntax/ext/tt/transcribe.rs8
-rw-r--r--src/libsyntax/feature_gate.rs20
-rw-r--r--src/libsyntax/fold.rs45
-rw-r--r--src/libsyntax/parse/attr.rs5
-rw-r--r--src/libsyntax/parse/lexer/mod.rs2
-rw-r--r--src/libsyntax/parse/mod.rs12
-rw-r--r--src/libsyntax/parse/parser.rs119
-rw-r--r--src/libsyntax/parse/token.rs69
-rw-r--r--src/libsyntax/print/pprust.rs94
-rw-r--r--src/libsyntax/std_inject.rs6
-rw-r--r--src/libsyntax/test.rs10
-rw-r--r--src/libsyntax/util/node_count.rs4
-rw-r--r--src/libsyntax/visit.rs46
-rw-r--r--src/libsyntax_ext/assert.rs5
-rw-r--r--src/libsyntax_ext/cfg.rs2
-rw-r--r--src/libsyntax_ext/concat.rs2
-rw-r--r--src/libsyntax_ext/concat_idents.rs34
-rw-r--r--src/libsyntax_ext/deriving/clone.rs2
-rw-r--r--src/libsyntax_ext/deriving/debug.rs2
-rw-r--r--src/libsyntax_ext/deriving/encodable.rs2
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs20
-rw-r--r--src/libsyntax_ext/deriving/generic/ty.rs7
-rw-r--r--src/libsyntax_ext/env.rs2
-rw-r--r--src/libsyntax_ext/format.rs6
-rw-r--r--src/libsyntax_ext/proc_macro_registrar.rs4
-rw-r--r--src/libsyntax_pos/hygiene.rs6
-rw-r--r--src/libsyntax_pos/lib.rs48
-rw-r--r--src/libsyntax_pos/symbol.rs48
-rw-r--r--src/test/compile-fail-fulldeps/auxiliary/macro_crate_test.rs2
-rw-r--r--src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs2
-rw-r--r--src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs31
-rw-r--r--src/test/ui/feature-gate-non_ascii_idents.stderr38
76 files changed, 791 insertions, 858 deletions
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index ebd5c834fd0..32697e46a08 100644
--- a/src/libproc_macro/lib.rs
+++ b/src/libproc_macro/lib.rs
@@ -1097,15 +1097,17 @@ impl TokenTree {
                 }).into();
             },
             self::TokenTree::Term(tt) => {
-                let ident = ast::Ident { name: tt.sym, ctxt: tt.span.0.ctxt() };
+                let ident = ast::Ident::new(tt.sym, tt.span.0);
                 let sym_str = tt.sym.as_str();
-                let token =
-                    if sym_str.starts_with("'") { Lifetime(ident) }
-                    else if sym_str.starts_with("r#") {
-                        let name = Symbol::intern(&sym_str[2..]);
-                        let ident = ast::Ident { name, ctxt: tt.span.0.ctxt() };
-                        Ident(ident, true)
-                    } else { Ident(ident, false) };
+                let token = if sym_str.starts_with("'") {
+                    Lifetime(ident)
+                } else if sym_str.starts_with("r#") {
+                    let name = Symbol::intern(&sym_str[2..]);
+                    let ident = ast::Ident::new(name, ident.span);
+                    Ident(ident, true)
+                } else {
+                    Ident(ident, false)
+                };
                 return TokenTree::Token(tt.span.0, token).into();
             }
             self::TokenTree::Literal(self::Literal {
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index b767ede5faa..b13c289394a 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -909,7 +909,7 @@ impl<'a> LoweringContext<'a> {
 
     fn lower_ident(&mut self, ident: Ident) -> Name {
         let ident = ident.modern();
-        if ident.ctxt == SyntaxContext::empty() {
+        if ident.span.ctxt() == SyntaxContext::empty() {
             return ident.name;
         }
         *self.name_map
@@ -920,7 +920,7 @@ impl<'a> LoweringContext<'a> {
     fn lower_label(&mut self, label: Option<Label>) -> Option<hir::Label> {
         label.map(|label| hir::Label {
             name: label.ident.name,
-            span: label.span,
+            span: label.ident.span,
         })
     }
 
@@ -1358,7 +1358,7 @@ impl<'a> LoweringContext<'a> {
     fn lower_variant(&mut self, v: &Variant) -> hir::Variant {
         Spanned {
             node: hir::Variant_ {
-                name: v.node.name.name,
+                name: v.node.ident.name,
                 attrs: self.lower_attrs(&v.node.attrs),
                 data: self.lower_variant_data(&v.node.data),
                 disr_expr: v.node
@@ -1607,7 +1607,7 @@ impl<'a> LoweringContext<'a> {
         }
 
         hir::PathSegment::new(
-            self.lower_ident(segment.identifier),
+            self.lower_ident(segment.ident),
             parameters,
             infer_types,
         )
@@ -1720,7 +1720,7 @@ impl<'a> LoweringContext<'a> {
         decl.inputs
             .iter()
             .map(|arg| match arg.pat.node {
-                PatKind::Ident(_, ident, None) => respan(ident.span, ident.node.name),
+                PatKind::Ident(_, ident, None) => respan(ident.span, ident.name),
                 _ => respan(arg.pat.span, keywords::Invalid.name()),
             })
             .collect()
@@ -1810,7 +1810,7 @@ impl<'a> LoweringContext<'a> {
             default: tp.default
                 .as_ref()
                 .map(|x| self.lower_ty(x, ImplTraitContext::Disallowed)),
-            span: tp.span,
+            span: tp.ident.span,
             pure_wrt_drop: attr::contains_name(&tp.attrs, "may_dangle"),
             synthetic: tp.attrs
                 .iter()
@@ -1822,21 +1822,22 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
+        let span = l.ident.span;
         match self.lower_ident(l.ident) {
-            x if x == "'static" => self.new_named_lifetime(l.id, l.span, hir::LifetimeName::Static),
+            x if x == "'static" => self.new_named_lifetime(l.id, span, hir::LifetimeName::Static),
             x if x == "'_" => match self.anonymous_lifetime_mode {
                 AnonymousLifetimeMode::CreateParameter => {
-                    let fresh_name = self.collect_fresh_in_band_lifetime(l.span);
-                    self.new_named_lifetime(l.id, l.span, fresh_name)
+                    let fresh_name = self.collect_fresh_in_band_lifetime(span);
+                    self.new_named_lifetime(l.id, span, fresh_name)
                 }
 
                 AnonymousLifetimeMode::PassThrough => {
-                    self.new_named_lifetime(l.id, l.span, hir::LifetimeName::Underscore)
+                    self.new_named_lifetime(l.id, span, hir::LifetimeName::Underscore)
                 }
             },
             name => {
-                self.maybe_collect_in_band_lifetime(l.span, name);
-                self.new_named_lifetime(l.id, l.span, hir::LifetimeName::Name(name))
+                self.maybe_collect_in_band_lifetime(span, name);
+                self.new_named_lifetime(l.id, span, hir::LifetimeName::Name(name))
             }
         }
     }
@@ -2089,10 +2090,7 @@ impl<'a> LoweringContext<'a> {
             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(),
-                },
+                None => Ident::new(Symbol::intern(&index.to_string()), f.span),
             }),
             vis: self.lower_visibility(&f.vis, None),
             ty: self.lower_ty(&f.ty, ImplTraitContext::Disallowed),
@@ -2102,7 +2100,7 @@ impl<'a> LoweringContext<'a> {
 
     fn lower_field(&mut self, f: &Field) -> hir::Field {
         hir::Field {
-            name: respan(f.ident.span, self.lower_ident(f.ident.node)),
+            name: respan(f.ident.span, self.lower_ident(f.ident)),
             expr: P(self.lower_expr(&f.expr)),
             span: f.span,
             is_shorthand: f.is_shorthand,
@@ -2359,11 +2357,11 @@ impl<'a> LoweringContext<'a> {
 
                 // Correctly resolve `self` imports
                 if path.segments.len() > 1
-                    && path.segments.last().unwrap().identifier.name == keywords::SelfValue.name()
+                    && path.segments.last().unwrap().ident.name == keywords::SelfValue.name()
                 {
                     let _ = path.segments.pop();
                     if rename.is_none() {
-                        *name = path.segments.last().unwrap().identifier.name;
+                        *name = path.segments.last().unwrap().ident.name;
                     }
                 }
 
@@ -2804,7 +2802,7 @@ impl<'a> LoweringContext<'a> {
     fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> {
         let node = match p.node {
             PatKind::Wild => hir::PatKind::Wild,
-            PatKind::Ident(ref binding_mode, pth1, ref sub) => {
+            PatKind::Ident(ref binding_mode, ident, ref sub) => {
                 match self.resolver.get_resolution(p.id).map(|d| d.base_def()) {
                     // `None` can occur in body-less function signatures
                     def @ None | def @ Some(Def::Local(_)) => {
@@ -2815,16 +2813,16 @@ impl<'a> LoweringContext<'a> {
                         hir::PatKind::Binding(
                             self.lower_binding_mode(binding_mode),
                             canonical_id,
-                            respan(pth1.span, pth1.node.name),
+                            respan(ident.span, ident.name),
                             sub.as_ref().map(|x| self.lower_pat(x)),
                         )
                     }
                     Some(def) => hir::PatKind::Path(hir::QPath::Resolved(
                         None,
                         P(hir::Path {
-                            span: pth1.span,
+                            span: ident.span,
                             def,
-                            segments: hir_vec![hir::PathSegment::from_name(pth1.node.name)],
+                            segments: hir_vec![hir::PathSegment::from_name(ident.name)],
                         }),
                     )),
                 }
@@ -2939,7 +2937,7 @@ impl<'a> LoweringContext<'a> {
                     ImplTraitContext::Disallowed,
                 );
                 let args = args.iter().map(|x| self.lower_expr(x)).collect();
-                hir::ExprMethodCall(hir_seg, seg.span, args)
+                hir::ExprMethodCall(hir_seg, seg.ident.span, args)
             }
             ExprKind::Binary(binop, ref lhs, ref rhs) => {
                 let binop = self.lower_binop(binop);
@@ -3074,7 +3072,7 @@ impl<'a> LoweringContext<'a> {
             ),
             ExprKind::Field(ref el, ident) => hir::ExprField(
                 P(self.lower_expr(el)),
-                respan(ident.span, self.lower_ident(ident.node)),
+                respan(ident.span, self.lower_ident(ident)),
             ),
             ExprKind::TupField(ref el, ident) => hir::ExprTupField(P(self.lower_expr(el)), ident),
             ExprKind::Index(ref el, ref er) => {
@@ -3505,12 +3503,10 @@ impl<'a> LoweringContext<'a> {
                 let attr = {
                     // allow(unreachable_code)
                     let allow = {
-                        let allow_ident = self.str_to_ident("allow");
-                        let uc_ident = self.str_to_ident("unreachable_code");
-                        let uc_meta_item = attr::mk_spanned_word_item(e.span, uc_ident);
-                        let uc_nested = NestedMetaItemKind::MetaItem(uc_meta_item);
-                        let uc_spanned = respan(e.span, uc_nested);
-                        attr::mk_spanned_list_item(e.span, allow_ident, vec![uc_spanned])
+                        let allow_ident = Ident::from_str("allow").with_span_pos(e.span);
+                        let uc_ident = Ident::from_str("unreachable_code").with_span_pos(e.span);
+                        let uc_nested = attr::mk_nested_word_item(uc_ident);
+                        attr::mk_list_item(e.span, allow_ident, vec![uc_nested])
                     };
                     attr::mk_spanned_attr_outer(e.span, attr::mk_attr_id(), allow)
                 };
diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs
index 20f46cb348d..4bd857c73e6 100644
--- a/src/librustc/hir/map/def_collector.rs
+++ b/src/librustc/hir/map/def_collector.rs
@@ -132,7 +132,7 @@ 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.ident.name.as_str()),
                                             REGULAR_SPACE,
                                             v.span);
                         this.with_parent(variant_def_index, |this| {
@@ -202,7 +202,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
                     lifetime_def.lifetime.id,
                     DefPathData::LifetimeDef(lifetime_def.lifetime.ident.name.as_str()),
                     REGULAR_SPACE,
-                    lifetime_def.lifetime.span
+                    lifetime_def.lifetime.ident.span
                 );
             }
             GenericParam::Type(ref ty_param) => {
@@ -210,7 +210,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
                     ty_param.id,
                     DefPathData::TypeParam(ty_param.ident.name.as_str()),
                     REGULAR_SPACE,
-                    ty_param.span
+                    ty_param.ident.span
                 );
             }
         }
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index c3b3e10201f..38b284fd646 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -654,11 +654,12 @@ impl<'a> HashStable<StableHashingContext<'a>> for ast::Ident {
                                           hcx: &mut StableHashingContext<'a>,
                                           hasher: &mut StableHasher<W>) {
         let ast::Ident {
-            ref name,
-            ctxt: _ // Ignore this
+            name,
+            span,
         } = *self;
 
         name.hash_stable(hcx, hasher);
+        span.hash_stable(hcx, hasher);
     }
 }
 
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs
index 425459f448f..3bb4c86e7c2 100644
--- a/src/librustc/ich/impls_syntax.rs
+++ b/src/librustc/ich/impls_syntax.rs
@@ -162,7 +162,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, ident });
+impl_stable_hash_for!(struct ::syntax::ast::Lifetime { id, ident });
 impl_stable_hash_for!(enum ::syntax::ast::StrStyle { Cooked, Raw(pounds) });
 impl_stable_hash_for!(enum ::syntax::ast::AttrStyle { Outer, Inner });
 
@@ -211,7 +211,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute {
         style.hash_stable(hcx, hasher);
         path.segments.len().hash_stable(hcx, hasher);
         for segment in &path.segments {
-            segment.identifier.name.hash_stable(hcx, hasher);
+            segment.ident.name.hash_stable(hcx, hasher);
         }
         for tt in tokens.trees() {
             tt.hash_stable(hcx, hasher);
@@ -341,7 +341,7 @@ impl_stable_hash_for!(enum ::syntax::ast::NestedMetaItemKind {
 });
 
 impl_stable_hash_for!(struct ::syntax::ast::MetaItem {
-    name,
+    ident,
     node,
     span
 });
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 3c833251f72..9f8cc2f8699 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -952,8 +952,8 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
         ast_visit::walk_ty(self, t);
     }
 
-    fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
-        run_lints!(self, check_ident, early_passes, sp, id);
+    fn visit_ident(&mut self, ident: ast::Ident) {
+        run_lints!(self, check_ident, early_passes, ident);
     }
 
     fn visit_mod(&mut self, m: &'a ast::Mod, s: Span, _a: &[ast::Attribute], n: ast::NodeId) {
diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs
index 8a899a35ecb..e8b536d5267 100644
--- a/src/librustc/lint/levels.rs
+++ b/src/librustc/lint/levels.rs
@@ -221,7 +221,7 @@ impl<'a> LintLevelsBuilder<'a> {
                         continue
                     }
                 };
-                let name = word.name();
+                let name = word.ident.name;
                 match store.check_lint_name(&name.as_str()) {
                     CheckLintNameResult::Ok(ids) => {
                         let src = LintSource::Node(name, li.span);
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index 1497be2d5ba..89a9f303478 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -236,7 +236,7 @@ pub trait LateLintPass<'a, 'tcx>: LintPass {
 }
 
 pub trait EarlyLintPass: LintPass {
-    fn check_ident(&mut self, _: &EarlyContext, _: Span, _: ast::Ident) { }
+    fn check_ident(&mut self, _: &EarlyContext, _: ast::Ident) { }
     fn check_crate(&mut self, _: &EarlyContext, _: &ast::Crate) { }
     fn check_crate_post(&mut self, _: &EarlyContext, _: &ast::Crate) { }
     fn check_mod(&mut self, _: &EarlyContext, _: &ast::Mod, _: Span, _: ast::NodeId) { }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 59d1a298eaa..8cb87e7e080 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1683,12 +1683,12 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> ast::CrateConfig {
             } else if meta_item.is_meta_item_list() {
                 let msg = format!(
                     "invalid predicate in --cfg command line argument: `{}`",
-                    meta_item.name()
+                    meta_item.ident
                 );
                 early_error(ErrorOutputType::default(), &msg)
             }
 
-            (meta_item.name(), meta_item.value_str())
+            (meta_item.ident.name, meta_item.value_str())
         })
         .collect::<ast::CrateConfig>()
 }
diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs
index 8c2c1cfa454..a1018cb946a 100644
--- a/src/librustc/traits/on_unimplemented.rs
+++ b/src/librustc/traits/on_unimplemented.rs
@@ -190,7 +190,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
         for command in self.subcommands.iter().chain(Some(self)).rev() {
             if let Some(ref condition) = command.condition {
                 if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
-                    options.contains(&(c.name().as_str().to_string(),
+                    options.contains(&(c.ident.name.as_str().to_string(),
                                       match c.value_str().map(|s| s.as_str().to_string()) {
                                           Some(s) => Some(s),
                                           None => None
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 72ba199eb24..b920553ec36 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -2088,8 +2088,8 @@ impl<'a, 'gcx, 'tcx> VariantDef {
             return Some(index);
         }
         let mut ident = name.to_ident();
-        while ident.ctxt != SyntaxContext::empty() {
-            ident.ctxt.remove_mark();
+        while ident.span.ctxt() != SyntaxContext::empty() {
+            ident.span.remove_mark();
             if let Some(field) = self.fields.iter().position(|f| f.name.to_ident() == ident) {
                 return Some(field);
             }
@@ -2558,7 +2558,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             LOCAL_CRATE => self.hir.definitions().expansion(scope.index),
             _ => Mark::root(),
         };
-        let scope = match ident.ctxt.adjust(expansion) {
+        let scope = match ident.span.adjust(expansion) {
             Some(macro_def) => self.hir.definitions().macro_def_scope(macro_def),
             None if block == DUMMY_NODE_ID => DefId::local(CRATE_DEF_INDEX), // Dummy DefId
             None => self.hir.get_module_parent(block),
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index b461431c7bb..6f88b0aecb6 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -1045,7 +1045,7 @@ impl RustcDefaultCalls {
                     let mut cfgs = Vec::new();
                     for &(name, ref value) in sess.parse_sess.config.iter() {
                         let gated_cfg = GatedCfg::gate(&ast::MetaItem {
-                            name,
+                            ident: ast::Ident::with_empty_ctxt(name),
                             node: ast::MetaItemKind::Word,
                             span: DUMMY_SP,
                         });
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index c5e7fdb30d3..70b73ebb8cd 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -466,11 +466,11 @@ impl<'a> PrinterSupport for HygieneAnnotation<'a> {
 impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
     fn post(&self, s: &mut pprust::State, node: pprust::AnnNode) -> io::Result<()> {
         match node {
-            pprust::NodeIdent(&ast::Ident { name, ctxt }) => {
+            pprust::NodeIdent(&ast::Ident { name, span }) => {
                 s.s.space()?;
                 // FIXME #16420: this doesn't display the connections
                 // between syntax contexts
-                s.synth_comment(format!("{}{:?}", name.as_u32(), ctxt))
+                s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
             }
             pprust::NodeName(&name) => {
                 s.s.space()?;
diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs
index 17a6176b79e..57311a7b588 100644
--- a/src/librustc_incremental/assert_dep_graph.rs
+++ b/src/librustc_incremental/assert_dep_graph.rs
@@ -110,7 +110,7 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
         for list_item in attr.meta_item_list().unwrap_or_default() {
             match list_item.word() {
                 Some(word) if value.is_none() =>
-                    value = Some(word.name().clone()),
+                    value = Some(word.ident.name),
                 _ =>
                     // FIXME better-encapsulate meta_item (don't directly access `node`)
                     span_bug!(list_item.span(), "unexpected meta-item {:?}", list_item.node),
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index c0728cb2b66..2cc6708bc03 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -171,16 +171,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
                 if fieldpat.node.is_shorthand {
                     continue;
                 }
-                if let PatKind::Binding(_, _, ident, None) = fieldpat.node.pat.node {
-                    if ident.node == fieldpat.node.name {
+                if let PatKind::Binding(_, _, name, None) = fieldpat.node.pat.node {
+                    if name.node == fieldpat.node.name {
                         let mut err = cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS,
                                      fieldpat.span,
                                      &format!("the `{}:` in this pattern is redundant",
-                                              ident.node));
+                                              name.node));
                         let subspan = cx.tcx.sess.codemap().span_through_char(fieldpat.span, ':');
                         err.span_suggestion_short(subspan,
                                                   "remove this",
-                                                  format!("{}", ident.node));
+                                                  format!("{}", name.node));
                         err.emit();
                     }
                 }
@@ -625,7 +625,7 @@ impl EarlyLintPass for AnonymousParameters {
                 for arg in sig.decl.inputs.iter() {
                     match arg.pat.node {
                         ast::PatKind::Ident(_, ident, None) => {
-                            if ident.node.name == keywords::Invalid.name() {
+                            if ident.name == keywords::Invalid.name() {
                                 cx.span_lint(ANONYMOUS_PARAMETERS,
                                              arg.pat.span,
                                              "use of deprecated anonymous parameter");
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index aa93b3098e0..bf86f6a6952 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -377,7 +377,7 @@ impl UnusedImportBraces {
             let node_ident;
             match items[0].0.kind {
                 ast::UseTreeKind::Simple(rename) => {
-                    let orig_ident = items[0].0.prefix.segments.last().unwrap().identifier;
+                    let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
                     if orig_ident.name == keywords::SelfValue.name() {
                         return;
                     } else {
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 6f5fcc9e421..9b0e4c64171 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -467,8 +467,8 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
                 mutability: Mutability::Not,
             };
             if let Some(hir::map::NodeBinding(pat)) = tcx.hir.find(var_id) {
-                if let hir::PatKind::Binding(_, _, ref ident, _) = pat.node {
-                    decl.debug_name = ident.node;
+                if let hir::PatKind::Binding(_, _, ref name, _) = pat.node {
+                    decl.debug_name = name.node;
 
                     let bm = *hir.tables.pat_binding_modes()
                                         .get(pat.hir_id)
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index 74e41ef9c93..454b6cbd27d 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -152,7 +152,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitD
                 } else {
                     sess.span_err(
                         item.span,
-                        &format!("{} attribute requires a path", item.name()));
+                        &format!("{} attribute requires a path", item.ident));
                     return None;
                 }
             }
diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs
index 798d6353181..c3f41e8ac48 100644
--- a/src/librustc_mir/hair/pattern/mod.rs
+++ b/src/librustc_mir/hair/pattern/mod.rs
@@ -466,7 +466,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
                 }
             }
 
-            PatKind::Binding(_, id, ref ident, ref sub) => {
+            PatKind::Binding(_, id, ref name, ref sub) => {
                 let var_ty = self.tables.node_id_to_type(pat.hir_id);
                 let region = match var_ty.sty {
                     ty::TyRef(r, _) => Some(r),
@@ -493,14 +493,14 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
                     if let ty::TyRef(_, mt) = ty.sty {
                         ty = mt.ty;
                     } else {
-                        bug!("`ref {}` has wrong type {}", ident.node, ty);
+                        bug!("`ref {}` has wrong type {}", name.node, ty);
                     }
                 }
 
                 PatternKind::Binding {
                     mutability,
                     mode,
-                    name: ident.node,
+                    name: name.node,
                     var: id,
                     ty: var_ty,
                     subpattern: self.lower_opt_pattern(sub),
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index b34decc1c69..1866122454c 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -36,19 +36,20 @@ impl<'a> AstValidator<'a> {
         &self.session.parse_sess.span_diagnostic
     }
 
-    fn check_lifetime(&self, lifetime: &Lifetime) {
+    fn check_lifetime(&self, ident: Ident) {
         let valid_names = [keywords::UnderscoreLifetime.name(),
                            keywords::StaticLifetime.name(),
                            keywords::Invalid.name()];
-        if !valid_names.contains(&lifetime.ident.name) &&
-            token::is_reserved_ident(lifetime.ident.without_first_quote()) {
-            self.err_handler().span_err(lifetime.span, "lifetimes cannot use keyword names");
+        if !valid_names.contains(&ident.name) &&
+            token::is_reserved_ident(ident.without_first_quote()) {
+            self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names");
         }
     }
 
-    fn check_label(&self, label: Ident, span: Span) {
-        if token::is_reserved_ident(label.without_first_quote()) {
-            self.err_handler().span_err(span, &format!("invalid label name `{}`", label.name));
+    fn check_label(&self, ident: Ident) {
+        if token::is_reserved_ident(ident.without_first_quote()) {
+            self.err_handler()
+                .span_err(ident.span, &format!("invalid label name `{}`", ident.name));
         }
     }
 
@@ -144,7 +145,7 @@ impl<'a> AstValidator<'a> {
         let non_lifetime_param_spans : Vec<_> = params.iter()
             .filter_map(|param| match *param {
                 GenericParam::Lifetime(_) => None,
-                GenericParam::Type(ref t) => Some(t.span),
+                GenericParam::Type(ref t) => Some(t.ident.span),
             }).collect();
         if !non_lifetime_param_spans.is_empty() {
             self.err_handler().span_err(non_lifetime_param_spans,
@@ -156,7 +157,7 @@ impl<'a> AstValidator<'a> {
             match *param {
                 GenericParam::Lifetime(ref l) => {
                     if !l.bounds.is_empty() {
-                        let spans : Vec<_> = l.bounds.iter().map(|b| b.span).collect();
+                        let spans: Vec<_> = l.bounds.iter().map(|b| b.ident.span).collect();
                         self.err_handler().span_err(spans,
                             "lifetime bounds cannot be used in this context");
                     }
@@ -193,7 +194,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 for bound in bounds {
                     if let RegionTyParamBound(ref lifetime) = *bound {
                         if any_lifetime_bounds {
-                            span_err!(self.session, lifetime.span, E0226,
+                            span_err!(self.session, lifetime.ident.span, E0226,
                                       "only a single explicit lifetime bound is permitted");
                             break;
                         }
@@ -234,12 +235,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
     }
 
     fn visit_label(&mut self, label: &'a Label) {
-        self.check_label(label.ident, label.span);
+        self.check_label(label.ident);
         visit::walk_label(self, label);
     }
 
     fn visit_lifetime(&mut self, lifetime: &'a Lifetime) {
-        self.check_lifetime(lifetime);
+        self.check_lifetime(lifetime.ident);
         visit::walk_lifetime(self, lifetime);
     }
 
@@ -328,19 +329,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             ItemKind::TraitAlias(Generics { ref params, .. }, ..) => {
                 for param in params {
                     if let GenericParam::Type(TyParam {
+                        ident,
                         ref bounds,
                         ref default,
-                        span,
                         ..
                     }) = *param
                     {
                         if !bounds.is_empty() {
-                            self.err_handler().span_err(span,
+                            self.err_handler().span_err(ident.span,
                                                         "type parameters on the left side of a \
                                                          trait alias cannot be bounded");
                         }
                         if !default.is_none() {
-                            self.err_handler().span_err(span,
+                            self.err_handler().span_err(ident.span,
                                                         "type parameters on the left side of a \
                                                          trait alias cannot have defaults");
                         }
@@ -408,7 +409,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             match (param, seen_non_lifetime_param) {
                 (&GenericParam::Lifetime(ref ld), true) => {
                     self.err_handler()
-                        .span_err(ld.lifetime.span, "lifetime parameters must be leading");
+                        .span_err(ld.lifetime.ident.span, "lifetime parameters must be leading");
                 },
                 (&GenericParam::Lifetime(_), false) => {}
                 _ => {
@@ -417,7 +418,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             }
 
             if let GenericParam::Type(ref ty_param @ TyParam { default: Some(_), .. }) = *param {
-                seen_default = Some(ty_param.span);
+                seen_default = Some(ty_param.ident.span);
             } else if let Some(span) = seen_default {
                 self.err_handler()
                     .span_err(span, "type parameters with a default must be trailing");
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 95be691b30c..d951a7f1cc1 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -35,7 +35,6 @@ use rustc::util::nodemap::NodeSet;
 use syntax::ast::{self, CRATE_NODE_ID, Ident};
 use syntax::symbol::keywords;
 use syntax_pos::Span;
-use syntax_pos::hygiene::SyntaxContext;
 
 use std::cmp;
 use std::mem::replace;
@@ -495,11 +494,11 @@ struct NamePrivacyVisitor<'a, 'tcx: 'a> {
 impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> {
     // Checks that a field in a struct constructor (expression or pattern) is accessible.
     fn check_field(&mut self,
-                   use_ctxt: SyntaxContext, // Syntax context of the field name at the use site
+                   use_ctxt: Span, // Syntax context of the field name at the use site
                    span: Span, // Span of the field pattern, e.g. `x: 0`
                    def: &'tcx ty::AdtDef, // Definition of the struct or enum
                    field: &'tcx ty::FieldDef) { // Definition of the field
-        let ident = Ident { ctxt: use_ctxt.modern(), ..keywords::Invalid.ident() };
+        let ident = Ident::new(keywords::Invalid.name(), use_ctxt.modern());
         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",
@@ -573,14 +572,14 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
                     for variant_field in &variant.fields {
                         let field = fields.iter().find(|f| f.name.node == variant_field.name);
                         let (use_ctxt, span) = match field {
-                            Some(field) => (field.name.node.to_ident().ctxt, field.span),
-                            None => (base.span.ctxt(), base.span),
+                            Some(field) => (field.name.node.to_ident().span, field.span),
+                            None => (base.span, base.span),
                         };
                         self.check_field(use_ctxt, span, adt, variant_field);
                     }
                 } else {
                     for field in fields {
-                        let use_ctxt = field.name.node.to_ident().ctxt;
+                        let use_ctxt = field.name.node.to_ident().span;
                         let field_def = variant.field_named(field.name.node);
                         self.check_field(use_ctxt, field.span, adt, field_def);
                     }
@@ -599,7 +598,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
                 let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap();
                 let variant = adt.variant_of_def(def);
                 for field in fields {
-                    let use_ctxt = field.node.name.to_ident().ctxt;
+                    let use_ctxt = field.node.name.to_ident().span;
                     let field_def = variant.field_named(field.node.name);
                     self.check_field(use_ctxt, field.span, adt, field_def);
                 }
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 397590012fd..0542ca6fb24 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -34,7 +34,6 @@ use syntax::attr;
 
 use syntax::ast::{self, Block, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId};
 use syntax::ast::{Mutability, StmtKind, TraitItem, TraitItemKind, Variant};
-use syntax::codemap::respan;
 use syntax::ext::base::SyntaxExtension;
 use syntax::ext::base::Determinacy::Undetermined;
 use syntax::ext::hygiene::Mark;
@@ -115,13 +114,13 @@ impl<'a> Resolver<'a> {
 
         let mut module_path: Vec<_> = prefix.segments.iter()
             .chain(path.segments.iter())
-            .map(|seg| respan(seg.span, seg.identifier))
+            .map(|seg| seg.ident)
             .collect();
 
         match use_tree.kind {
             ast::UseTreeKind::Simple(rename) => {
                 let mut ident = use_tree.ident();
-                let mut source = module_path.pop().unwrap().node;
+                let mut source = module_path.pop().unwrap();
                 let mut type_ns_only = false;
 
                 if nested {
@@ -130,7 +129,7 @@ impl<'a> Resolver<'a> {
                         type_ns_only = true;
 
                         let last_segment = *module_path.last().unwrap();
-                        if last_segment.node.name == keywords::CrateRoot.name() {
+                        if last_segment.name == keywords::CrateRoot.name() {
                             resolve_error(
                                 self,
                                 use_tree.span,
@@ -142,9 +141,9 @@ impl<'a> Resolver<'a> {
 
                         // Replace `use foo::self;` with `use foo;`
                         let _ = module_path.pop();
-                        source = last_segment.node;
+                        source = last_segment;
                         if rename.is_none() {
-                            ident = last_segment.node;
+                            ident = last_segment;
                         }
                     }
                 } else {
@@ -157,7 +156,7 @@ impl<'a> Resolver<'a> {
 
                     // Disallow `use $crate;`
                     if source.name == keywords::DollarCrate.name() && path.segments.len() == 1 {
-                        let crate_root = self.resolve_crate_root(source.ctxt, true);
+                        let crate_root = self.resolve_crate_root(source.span.ctxt(), true);
                         let crate_name = match crate_root.kind {
                             ModuleKind::Def(_, name) => name,
                             ModuleKind::Block(..) => unreachable!(),
@@ -195,12 +194,8 @@ impl<'a> Resolver<'a> {
             }
             ast::UseTreeKind::Nested(ref items) => {
                 let prefix = ast::Path {
-                    segments: module_path.iter()
-                        .map(|s| ast::PathSegment {
-                            identifier: s.node,
-                            span: s.span,
-                            parameters: None,
-                        })
+                    segments: module_path.into_iter()
+                        .map(|ident| ast::PathSegment::from_ident(ident))
                         .collect(),
                     span: path.span,
                 };
@@ -428,7 +423,7 @@ impl<'a> Resolver<'a> {
                                        parent: Module<'a>,
                                        vis: ty::Visibility,
                                        expansion: Mark) {
-        let ident = variant.node.name;
+        let ident = variant.node.ident;
         let def_id = self.definitions.local_def_id(variant.node.data.id());
 
         // Define a name in the type namespace.
@@ -722,7 +717,7 @@ impl<'a> Resolver<'a> {
                 match attr.meta_item_list() {
                     Some(names) => for attr in names {
                         if let Some(word) = attr.word() {
-                            imports.imports.push((word.name(), attr.span()));
+                            imports.imports.push((word.ident.name, attr.span()));
                         } else {
                             span_err!(self.session, attr.span(), E0466, "bad macro import");
                         }
@@ -736,7 +731,7 @@ impl<'a> Resolver<'a> {
                 if let Some(names) = attr.meta_item_list() {
                     for attr in names {
                         if let Some(word) = attr.word() {
-                            imports.reexports.push((word.name(), attr.span()));
+                            imports.reexports.push((word.ident.name, attr.span()));
                         } else {
                             bad_macro_reexport(self, attr.span());
                         }
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 64d57ea34c2..2bf17cd1317 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -41,9 +41,9 @@ use rustc::ty;
 use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
 use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap};
 
-use syntax::codemap::{dummy_spanned, respan, BytePos, CodeMap};
+use syntax::codemap::{BytePos, CodeMap};
 use syntax::ext::hygiene::{Mark, MarkKind, SyntaxContext};
-use syntax::ast::{self, Name, NodeId, Ident, SpannedIdent, FloatTy, IntTy, UintTy};
+use syntax::ast::{self, Name, NodeId, Ident, FloatTy, IntTy, UintTy};
 use syntax::ext::base::SyntaxExtension;
 use syntax::ext::base::Determinacy::{self, Determined, Undetermined};
 use syntax::ext::base::MacroKind;
@@ -851,7 +851,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
 
         // `visit::walk_variant` without the discriminant expression.
         self.visit_variant_data(&variant.node.data,
-                                variant.node.name,
+                                variant.node.ident,
                                 generics,
                                 item_id,
                                 variant.span);
@@ -1654,8 +1654,8 @@ impl<'a> Resolver<'a> {
         {
         let namespace = if is_value { ValueNS } else { TypeNS };
         let hir::Path { ref segments, span, ref mut def } = *path;
-        let path: Vec<SpannedIdent> = segments.iter()
-            .map(|seg| respan(span, Ident::with_empty_ctxt(seg.name)))
+        let path: Vec<Ident> = segments.iter()
+            .map(|seg| Ident::new(seg.name, span))
             .collect();
         match self.resolve_path(&path, Some(namespace), true, span) {
             PathResult::Module(module) => *def = module.def().unwrap(),
@@ -1912,10 +1912,11 @@ impl<'a> Resolver<'a> {
                                       path_span: Span)
                                       -> Option<LexicalScopeBinding<'a>> {
         if ns == TypeNS {
-            ident.ctxt = if ident.name == keywords::SelfType.name() {
-                SyntaxContext::empty() // FIXME(jseyfried) improve `Self` hygiene
+            ident.span = if ident.name == keywords::SelfType.name() {
+                // FIXME(jseyfried) improve `Self` hygiene
+                ident.span.with_ctxt(SyntaxContext::empty())
             } else {
-                ident.ctxt.modern()
+                ident.span.modern()
             }
         }
 
@@ -1931,10 +1932,10 @@ impl<'a> Resolver<'a> {
 
             module = match self.ribs[ns][i].kind {
                 ModuleRibKind(module) => module,
-                MacroDefinition(def) if def == self.macro_def(ident.ctxt) => {
+                MacroDefinition(def) if def == self.macro_def(ident.span.ctxt()) => {
                     // 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();
+                    ident.span.remove_mark();
                     continue
                 }
                 _ => continue,
@@ -1954,9 +1955,9 @@ impl<'a> Resolver<'a> {
             }
         }
 
-        ident.ctxt = ident.ctxt.modern();
+        ident.span = ident.span.modern();
         loop {
-            module = unwrap_or!(self.hygienic_lexical_parent(module, &mut ident.ctxt), break);
+            module = unwrap_or!(self.hygienic_lexical_parent(module, &mut ident.span), 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(
@@ -1980,10 +1981,10 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    fn hygienic_lexical_parent(&mut self, mut module: Module<'a>, ctxt: &mut SyntaxContext)
+    fn hygienic_lexical_parent(&mut self, mut module: Module<'a>, span: &mut Span)
                                -> Option<Module<'a>> {
-        if !module.expansion.is_descendant_of(ctxt.outer()) {
-            return Some(self.macro_def_scope(ctxt.remove_mark()));
+        if !module.expansion.is_descendant_of(span.ctxt().outer()) {
+            return Some(self.macro_def_scope(span.remove_mark()));
         }
 
         if let ModuleKind::Block(..) = module.kind {
@@ -1995,7 +1996,7 @@ impl<'a> Resolver<'a> {
             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()) {
+                return if parent_expansion.is_descendant_of(span.ctxt().outer()) {
                     Some(parent)
                 } else {
                     None
@@ -2016,9 +2017,9 @@ impl<'a> Resolver<'a> {
                                record_used: bool,
                                span: Span)
                                -> Result<&'a NameBinding<'a>, Determinacy> {
-        ident.ctxt = ident.ctxt.modern();
+        ident.span = ident.span.modern();
         let orig_current_module = self.current_module;
-        if let Some(def) = ident.ctxt.adjust(module.expansion) {
+        if let Some(def) = ident.span.adjust(module.expansion) {
             self.current_module = self.macro_def_scope(def);
         }
         let result = self.resolve_ident_in_module_unadjusted(
@@ -2108,8 +2109,8 @@ impl<'a> Resolver<'a> {
                 // 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 def == self.macro_def(ident.ctxt) {
-                        ident.ctxt.remove_mark();
+                    if def == self.macro_def(ident.span.ctxt()) {
+                        ident.span.remove_mark();
                     }
                 }
                 _ => {
@@ -2280,9 +2281,9 @@ impl<'a> Resolver<'a> {
                                 ident.name,
                                 span,
                             );
-                            resolve_error(self, type_parameter.span, err);
+                            resolve_error(self, type_parameter.ident.span, err);
                         }
-                        seen_bindings.entry(ident).or_insert(type_parameter.span);
+                        seen_bindings.entry(ident).or_insert(type_parameter.ident.span);
 
                         // plain insert (no renaming)
                         let def_id = self.definitions.local_def_id(type_parameter.id);
@@ -2350,7 +2351,7 @@ impl<'a> Resolver<'a> {
         let mut new_id = None;
         if let Some(trait_ref) = opt_trait_ref {
             let path: Vec<_> = trait_ref.path.segments.iter()
-                .map(|seg| respan(seg.span, seg.identifier))
+                .map(|seg| seg.ident)
                 .collect();
             let def = self.smart_resolve_path_fragment(
                 trait_ref.ref_id,
@@ -2499,7 +2500,7 @@ impl<'a> Resolver<'a> {
                     _ => false,
                 } {
                     let binding_info = BindingInfo { span: ident.span, binding_mode: binding_mode };
-                    binding_map.insert(ident.node, binding_info);
+                    binding_map.insert(ident, binding_info);
                 }
             }
             true
@@ -2639,7 +2640,7 @@ impl<'a> Resolver<'a> {
     }
 
     fn fresh_binding(&mut self,
-                     ident: &SpannedIdent,
+                     ident: Ident,
                      pat_id: NodeId,
                      outer_pat_id: NodeId,
                      pat_src: PatternSource,
@@ -2651,14 +2652,14 @@ impl<'a> Resolver<'a> {
         // because that breaks the assumptions later
         // passes make about or-patterns.)
         let mut def = Def::Local(pat_id);
-        match bindings.get(&ident.node).cloned() {
+        match bindings.get(&ident).cloned() {
             Some(id) if id == outer_pat_id => {
                 // `Variant(a, a)`, error
                 resolve_error(
                     self,
                     ident.span,
                     ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(
-                        &ident.node.name.as_str())
+                        &ident.name.as_str())
                 );
             }
             Some(..) if pat_src == PatternSource::FnParam => {
@@ -2667,7 +2668,7 @@ impl<'a> Resolver<'a> {
                     self,
                     ident.span,
                     ResolutionError::IdentifierBoundMoreThanOnceInParameterList(
-                        &ident.node.name.as_str())
+                        &ident.name.as_str())
                 );
             }
             Some(..) if pat_src == PatternSource::Match ||
@@ -2675,7 +2676,7 @@ impl<'a> Resolver<'a> {
                         pat_src == PatternSource::WhileLet => {
                 // `Variant1(a) | Variant2(a)`, ok
                 // Reuse definition from the first `a`.
-                def = self.ribs[ValueNS].last_mut().unwrap().bindings[&ident.node];
+                def = self.ribs[ValueNS].last_mut().unwrap().bindings[&ident];
             }
             Some(..) => {
                 span_bug!(ident.span, "two bindings with the same name from \
@@ -2683,9 +2684,9 @@ impl<'a> Resolver<'a> {
             }
             None => {
                 // A completely fresh binding, add to the lists if it's valid.
-                if ident.node.name != keywords::Invalid.name() {
-                    bindings.insert(ident.node, outer_pat_id);
-                    self.ribs[ValueNS].last_mut().unwrap().bindings.insert(ident.node, def);
+                if ident.name != keywords::Invalid.name() {
+                    bindings.insert(ident, outer_pat_id);
+                    self.ribs[ValueNS].last_mut().unwrap().bindings.insert(ident, def);
                 }
             }
         }
@@ -2703,10 +2704,10 @@ impl<'a> Resolver<'a> {
         let outer_pat_id = pat.id;
         pat.walk(&mut |pat| {
             match pat.node {
-                PatKind::Ident(bmode, ref ident, ref opt_pat) => {
+                PatKind::Ident(bmode, ident, ref opt_pat) => {
                     // First try to resolve the identifier as some existing
                     // entity, then fall back to a fresh binding.
-                    let binding = self.resolve_ident_in_lexical_scope(ident.node, ValueNS,
+                    let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS,
                                                                       false, pat.span)
                                       .and_then(LexicalScopeBinding::item);
                     let resolution = binding.map(NameBinding::def).and_then(|def| {
@@ -2718,7 +2719,7 @@ impl<'a> Resolver<'a> {
                             Def::Const(..) if is_syntactic_ambiguity => {
                                 // Disambiguate in favor of a unit struct/variant
                                 // or constant pattern.
-                                self.record_use(ident.node, ValueNS, binding.unwrap(), ident.span);
+                                self.record_use(ident, ValueNS, binding.unwrap(), ident.span);
                                 Some(PathResolution::new(def))
                             }
                             Def::StructCtor(..) | Def::VariantCtor(..) |
@@ -2732,7 +2733,7 @@ impl<'a> Resolver<'a> {
                                     self,
                                     ident.span,
                                     ResolutionError::BindingShadowsSomethingUnacceptable(
-                                        pat_src.descr(), ident.node.name, binding.unwrap())
+                                        pat_src.descr(), ident.name, binding.unwrap())
                                 );
                                 None
                             }
@@ -2785,7 +2786,7 @@ impl<'a> Resolver<'a> {
                           source: PathSource)
                           -> PathResolution {
         let segments = &path.segments.iter()
-            .map(|seg| respan(seg.span, seg.identifier))
+            .map(|seg| seg.ident)
             .collect::<Vec<_>>();
         self.smart_resolve_path_fragment(id, qself, segments, path.span, source)
     }
@@ -2793,7 +2794,7 @@ impl<'a> Resolver<'a> {
     fn smart_resolve_path_fragment(&mut self,
                                    id: NodeId,
                                    qself: Option<&QSelf>,
-                                   path: &[SpannedIdent],
+                                   path: &[Ident],
                                    span: Span,
                                    source: PathSource)
                                    -> PathResolution {
@@ -2813,11 +2814,11 @@ impl<'a> Resolver<'a> {
                  format!("not a {}", expected),
                  span)
             } else {
-                let item_str = path[path.len() - 1].node;
+                let item_str = path[path.len() - 1];
                 let item_span = path[path.len() - 1].span;
                 let (mod_prefix, mod_str) = if path.len() == 1 {
                     (format!(""), format!("this scope"))
-                } else if path.len() == 2 && path[0].node.name == keywords::CrateRoot.name() {
+                } else if path.len() == 2 && path[0].name == keywords::CrateRoot.name() {
                     (format!(""), format!("the crate root"))
                 } else {
                     let mod_path = &path[..path.len() - 1];
@@ -2851,10 +2852,10 @@ impl<'a> Resolver<'a> {
 
             // Try to lookup the name in more relaxed fashion for better error reporting.
             let ident = *path.last().unwrap();
-            let candidates = this.lookup_import_candidates(ident.node.name, ns, is_expected);
+            let candidates = this.lookup_import_candidates(ident.name, ns, is_expected);
             if candidates.is_empty() && is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
                 let enum_candidates =
-                    this.lookup_import_candidates(ident.node.name, ns, is_enum_variant);
+                    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::<Vec<_>>();
                 enum_candidates.sort();
@@ -2872,8 +2873,8 @@ impl<'a> Resolver<'a> {
                 }
             }
             if path.len() == 1 && this.self_type_is_available(span) {
-                if let Some(candidate) = this.lookup_assoc_candidate(ident.node, ns, is_expected) {
-                    let self_is_available = this.self_value_is_available(path[0].node.ctxt, span);
+                if let Some(candidate) = this.lookup_assoc_candidate(ident, ns, is_expected) {
+                    let self_is_available = this.self_value_is_available(path[0].span, span);
                     match candidate {
                         AssocSuggestion::Field => {
                             err.span_suggestion(span, "try",
@@ -2918,12 +2919,12 @@ impl<'a> Resolver<'a> {
                     (Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
                         ExprKind::Field(_, ident) => {
                             err.span_label(parent.span, format!("did you mean `{}::{}`?",
-                                                                 path_str, ident.node));
+                                                                 path_str, ident));
                             return (err, candidates);
                         }
                         ExprKind::MethodCall(ref segment, ..) => {
                             err.span_label(parent.span, format!("did you mean `{}::{}(...)`?",
-                                                                 path_str, segment.identifier));
+                                                                 path_str, segment.ident));
                             return (err, candidates);
                         }
                         _ => {}
@@ -3027,7 +3028,7 @@ impl<'a> Resolver<'a> {
                 // or `<T>::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().node;
+                    let item_name = *path.last().unwrap();
                     let traits = self.get_traits_containing_item(item_name, ns);
                     self.trait_map.insert(id, traits);
                 }
@@ -3084,9 +3085,9 @@ impl<'a> Resolver<'a> {
         if let Some(LexicalScopeBinding::Def(def)) = binding { def != Def::Err } else { false }
     }
 
-    fn self_value_is_available(&mut self, ctxt: SyntaxContext, span: Span) -> bool {
-        let ident = Ident { name: keywords::SelfValue.name(), ctxt: ctxt };
-        let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, false, span);
+    fn self_value_is_available(&mut self, self_span: Span, path_span: Span) -> bool {
+        let ident = Ident::new(keywords::SelfValue.name(), self_span);
+        let binding = self.resolve_ident_in_lexical_scope(ident, ValueNS, false, path_span);
         if let Some(LexicalScopeBinding::Def(def)) = binding { def != Def::Err } else { false }
     }
 
@@ -3094,7 +3095,7 @@ impl<'a> Resolver<'a> {
     fn resolve_qpath_anywhere(&mut self,
                               id: NodeId,
                               qself: Option<&QSelf>,
-                              path: &[SpannedIdent],
+                              path: &[Ident],
                               primary_ns: Namespace,
                               span: Span,
                               defer_to_typeck: bool,
@@ -3114,10 +3115,10 @@ impl<'a> Resolver<'a> {
                 };
             }
         }
-        let is_global = self.global_macros.get(&path[0].node.name).cloned()
+        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].node.modern())) {
+                                     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))
@@ -3130,7 +3131,7 @@ impl<'a> Resolver<'a> {
     fn resolve_qpath(&mut self,
                      id: NodeId,
                      qself: Option<&QSelf>,
-                     path: &[SpannedIdent],
+                     path: &[Ident],
                      ns: Namespace,
                      span: Span,
                      global_by_default: bool)
@@ -3171,8 +3172,8 @@ impl<'a> Resolver<'a> {
             PathResult::Module(..) | PathResult::Failed(..)
                     if (ns == TypeNS || path.len() > 1) &&
                        self.primitive_type_table.primitive_types
-                           .contains_key(&path[0].node.name) => {
-                let prim = self.primitive_type_table.primitive_types[&path[0].node.name];
+                           .contains_key(&path[0].name) => {
+                let prim = self.primitive_type_table.primitive_types[&path[0].name];
                 PathResolution::with_unresolved_segments(Def::PrimTy(prim), path.len() - 1)
             }
             PathResult::Module(module) => PathResolution::new(module.def().unwrap()),
@@ -3185,8 +3186,8 @@ impl<'a> Resolver<'a> {
         };
 
         if path.len() > 1 && !global_by_default && result.base_def() != Def::Err &&
-           path[0].node.name != keywords::CrateRoot.name() &&
-           path[0].node.name != keywords::DollarCrate.name() {
+           path[0].name != keywords::CrateRoot.name() &&
+           path[0].name != keywords::DollarCrate.name() {
             let unqualified_result = {
                 match self.resolve_path(&[*path.last().unwrap()], Some(ns), false, span) {
                     PathResult::NonModule(path_res) => path_res.base_def(),
@@ -3204,7 +3205,7 @@ impl<'a> Resolver<'a> {
     }
 
     fn resolve_path(&mut self,
-                    path: &[SpannedIdent],
+                    path: &[Ident],
                     opt_ns: Option<Namespace>, // `None` indicates a module path
                     record_used: bool,
                     path_span: Span)
@@ -3216,14 +3217,14 @@ impl<'a> Resolver<'a> {
             debug!("resolve_path ident {} {:?}", i, ident);
             let is_last = i == path.len() - 1;
             let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS };
-            let name = ident.node.name;
+            let name = ident.name;
 
             if i == 0 && ns == TypeNS && name == keywords::SelfValue.name() {
-                let mut ctxt = ident.node.ctxt.modern();
+                let mut ctxt = ident.span.ctxt().modern();
                 module = Some(self.resolve_self(&mut ctxt, self.current_module));
                 continue
             } else if allow_super && ns == TypeNS && name == keywords::Super.name() {
-                let mut ctxt = ident.node.ctxt.modern();
+                let mut ctxt = ident.span.ctxt().modern();
                 let self_module = match i {
                     0 => self.resolve_self(&mut ctxt, self.current_module),
                     _ => module.unwrap(),
@@ -3243,16 +3244,16 @@ impl<'a> Resolver<'a> {
             if ns == TypeNS {
                 if (i == 0 && name == keywords::CrateRoot.name()) ||
                    (i == 1 && name == keywords::Crate.name() &&
-                              path[0].node.name == keywords::CrateRoot.name()) {
+                              path[0].name == keywords::CrateRoot.name()) {
                     // `::a::b` or `::crate::a::b`
-                    module = Some(self.resolve_crate_root(ident.node.ctxt, false));
+                    module = Some(self.resolve_crate_root(ident.span.ctxt(), false));
                     continue
                 } else if i == 0 && name == keywords::DollarCrate.name() {
                     // `$crate::a::b`
-                    module = Some(self.resolve_crate_root(ident.node.ctxt, true));
+                    module = Some(self.resolve_crate_root(ident.span.ctxt(), true));
                     continue
-                } else if i == 1 && !token::is_path_segment_keyword(ident.node) {
-                    let prev_name = path[0].node.name;
+                } else if i == 1 && !token::is_path_segment_keyword(ident) {
+                    let prev_name = path[0].name;
                     if prev_name == keywords::Extern.name() ||
                        prev_name == keywords::CrateRoot.name() &&
                        self.session.features_untracked().extern_absolute_paths {
@@ -3275,13 +3276,13 @@ impl<'a> Resolver<'a> {
                name == keywords::Super.name() && i != 0 ||
                name == keywords::Extern.name() && i != 0 ||
                name == keywords::Crate.name() && i != 1 &&
-                    path[0].node.name != keywords::CrateRoot.name() {
+                    path[0].name != keywords::CrateRoot.name() {
                 let name_str = if name == keywords::CrateRoot.name() {
                     format!("crate root")
                 } else {
                     format!("`{}`", name)
                 };
-                let msg = if i == 1 && path[0].node.name == keywords::CrateRoot.name() {
+                let msg = if i == 1 && path[0].name == keywords::CrateRoot.name() {
                     format!("global paths cannot start with {}", name_str)
                 } else if i == 0 && name == keywords::Crate.name() {
                     format!("{} can only be used in absolute paths", name_str)
@@ -3292,12 +3293,12 @@ impl<'a> Resolver<'a> {
             }
 
             let binding = if let Some(module) = module {
-                self.resolve_ident_in_module(module, ident.node, ns, false, record_used, path_span)
+                self.resolve_ident_in_module(module, ident, ns, false, record_used, path_span)
             } else if opt_ns == Some(MacroNS) {
-                self.resolve_lexical_macro_path_segment(ident.node, ns, record_used, path_span)
+                self.resolve_lexical_macro_path_segment(ident, ns, record_used, path_span)
                     .map(MacroBinding::binding)
             } else {
-                match self.resolve_ident_in_lexical_scope(ident.node, ns, record_used, path_span) {
+                match self.resolve_ident_in_lexical_scope(ident, ns, record_used, path_span) {
                     Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
                     Some(LexicalScopeBinding::Def(def))
                             if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => {
@@ -3323,7 +3324,7 @@ impl<'a> Resolver<'a> {
                         ));
                     } else {
                         return PathResult::Failed(ident.span,
-                                                  format!("Not a module `{}`", ident.node),
+                                                  format!("Not a module `{}`", ident),
                                                   is_last);
                     }
                 }
@@ -3344,12 +3345,12 @@ impl<'a> Resolver<'a> {
                         if let Some(candidate) = candidates.get(0) {
                             format!("Did you mean `{}`?", candidate.path)
                         } else {
-                            format!("Maybe a missing `extern crate {};`?", ident.node)
+                            format!("Maybe a missing `extern crate {};`?", ident)
                         }
                     } else if i == 0 {
-                        format!("Use of undeclared type or module `{}`", ident.node)
+                        format!("Use of undeclared type or module `{}`", ident)
                     } else {
-                        format!("Could not find `{}` in `{}`", ident.node, path[i - 1].node)
+                        format!("Could not find `{}` in `{}`", ident, path[i - 1])
                     };
                     return PathResult::Failed(ident.span, msg, is_last);
                 }
@@ -3515,7 +3516,7 @@ impl<'a> Resolver<'a> {
     }
 
     fn lookup_typo_candidate<FilterFn>(&mut self,
-                                       path: &[SpannedIdent],
+                                       path: &[Ident],
                                        ns: Namespace,
                                        filter_fn: FilterFn,
                                        span: Span)
@@ -3576,7 +3577,7 @@ impl<'a> Resolver<'a> {
             }
         }
 
-        let name = path[path.len() - 1].node.name;
+        let name = path[path.len() - 1].name;
         // Make sure error reporting is deterministic.
         names.sort_by_key(|name| name.as_str());
         match find_best_match_for_name(names.iter(), &name.as_str(), None) {
@@ -3633,7 +3634,7 @@ impl<'a> Resolver<'a> {
                         });
                         self.record_def(expr.id, err_path_resolution());
                         resolve_error(self,
-                                      label.span,
+                                      label.ident.span,
                                       ResolutionError::UndeclaredLabel(&label.ident.name.as_str(),
                                                                        close_match));
                     }
@@ -3738,18 +3739,18 @@ impl<'a> Resolver<'a> {
 
     fn record_candidate_traits_for_expr_if_necessary(&mut self, expr: &Expr) {
         match expr.node {
-            ExprKind::Field(_, name) => {
+            ExprKind::Field(_, ident) => {
                 // FIXME(#6890): Even though you can't treat a method like 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, ValueNS);
+                let traits = self.get_traits_containing_item(ident, ValueNS);
                 self.trait_map.insert(expr.id, traits);
             }
             ExprKind::MethodCall(ref segment, ..) => {
                 debug!("(recording candidate traits for expr) recording traits for {}",
                        expr.id);
-                let traits = self.get_traits_containing_item(segment.identifier, ValueNS);
+                let traits = self.get_traits_containing_item(segment.ident, ValueNS);
                 self.trait_map.insert(expr.id, traits);
             }
             _ => {
@@ -3771,12 +3772,12 @@ impl<'a> Resolver<'a> {
             }
         }
 
-        ident.ctxt = ident.ctxt.modern();
+        ident.span = ident.span.modern();
         let mut search_module = self.current_module;
         loop {
             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);
+                unwrap_or!(self.hygienic_lexical_parent(search_module, &mut ident.span), break);
         }
 
         if let Some(prelude) = self.prelude {
@@ -3808,7 +3809,7 @@ impl<'a> Resolver<'a> {
         for &(trait_name, binding) in traits.as_ref().unwrap().iter() {
             let module = binding.module().unwrap();
             let mut ident = ident;
-            if ident.ctxt.glob_adjust(module.expansion, binding.span.ctxt().modern()).is_none() {
+            if ident.span.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)
@@ -3864,7 +3865,7 @@ impl<'a> Resolver<'a> {
                     if filter_fn(name_binding.def()) {
                         // create the path
                         let mut segms = path_segments.clone();
-                        segms.push(ast::PathSegment::from_ident(ident, name_binding.span));
+                        segms.push(ast::PathSegment::from_ident(ident));
                         let path = Path {
                             span: name_binding.span,
                             segments: segms,
@@ -3886,7 +3887,7 @@ impl<'a> Resolver<'a> {
                 if let Some(module) = name_binding.module() {
                     // form the path
                     let mut path_segments = path_segments.clone();
-                    path_segments.push(ast::PathSegment::from_ident(ident, name_binding.span));
+                    path_segments.push(ast::PathSegment::from_ident(ident));
 
                     if !in_module_is_extern || name_binding.vis == ty::Visibility::Public {
                         // add the module to the lookup
@@ -3925,7 +3926,7 @@ impl<'a> Resolver<'a> {
                 if let Some(module) = name_binding.module() {
                     // form the path
                     let mut path_segments = path_segments.clone();
-                    path_segments.push(ast::PathSegment::from_ident(ident, name_binding.span));
+                    path_segments.push(ast::PathSegment::from_ident(ident));
                     if module.def() == Some(module_def) {
                         let path = Path {
                             span: name_binding.span,
@@ -3957,7 +3958,7 @@ impl<'a> Resolver<'a> {
             enum_module.for_each_child_stable(|ident, _, name_binding| {
                 if let Def::Variant(..) = name_binding.def() {
                     let mut segms = enum_import_suggestion.path.segments.clone();
-                    segms.push(ast::PathSegment::from_ident(ident, name_binding.span));
+                    segms.push(ast::PathSegment::from_ident(ident));
                     variants.push(Path {
                         span: name_binding.span,
                         segments: segms,
@@ -3987,7 +3988,7 @@ impl<'a> Resolver<'a> {
             ast::VisibilityKind::Restricted { ref path, id, .. } => {
                 // Visibilities are resolved as global by default, add starting root segment.
                 let segments = path.make_root().iter().chain(path.segments.iter())
-                    .map(|seg| respan(seg.span, seg.identifier))
+                    .map(|seg| seg.ident)
                     .collect::<Vec<_>>();
                 let def = self.smart_resolve_path_fragment(id, None, &segments, path.span,
                                                            PathSource::Visibility).base_def();
@@ -4221,7 +4222,7 @@ impl<'a> Resolver<'a> {
             if attr.path.segments.len() > 1 {
                 continue
             }
-            let ident = attr.path.segments[0].identifier;
+            let ident = attr.path.segments[0].ident;
             let result = self.resolve_lexical_macro_path_segment(ident,
                                                                  MacroNS,
                                                                  false,
@@ -4243,30 +4244,30 @@ impl<'a> Resolver<'a> {
     }
 }
 
-fn is_self_type(path: &[SpannedIdent], namespace: Namespace) -> bool {
-    namespace == TypeNS && path.len() == 1 && path[0].node.name == keywords::SelfType.name()
+fn is_self_type(path: &[Ident], namespace: Namespace) -> bool {
+    namespace == TypeNS && path.len() == 1 && path[0].name == keywords::SelfType.name()
 }
 
-fn is_self_value(path: &[SpannedIdent], namespace: Namespace) -> bool {
-    namespace == ValueNS && path.len() == 1 && path[0].node.name == keywords::SelfValue.name()
+fn is_self_value(path: &[Ident], namespace: Namespace) -> bool {
+    namespace == ValueNS && path.len() == 1 && path[0].name == keywords::SelfValue.name()
 }
 
-fn names_to_string(idents: &[SpannedIdent]) -> String {
+fn names_to_string(idents: &[Ident]) -> String {
     let mut result = String::new();
     for (i, ident) in idents.iter()
-                            .filter(|i| i.node.name != keywords::CrateRoot.name())
+                            .filter(|ident| ident.name != keywords::CrateRoot.name())
                             .enumerate() {
         if i > 0 {
             result.push_str("::");
         }
-        result.push_str(&ident.node.name.as_str());
+        result.push_str(&ident.name.as_str());
     }
     result
 }
 
 fn path_names_to_string(path: &Path) -> String {
     names_to_string(&path.segments.iter()
-                        .map(|seg| respan(seg.span, seg.identifier))
+                        .map(|seg| seg.ident)
                         .collect::<Vec<_>>())
 }
 
@@ -4355,7 +4356,6 @@ fn module_to_string(module: Module) -> Option<String> {
     }
     Some(names_to_string(&names.into_iter()
                         .rev()
-                        .map(|n| dummy_spanned(n))
                         .collect::<Vec<_>>()))
 }
 
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 0692a1e0d7f..0388465b485 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -20,7 +20,6 @@ use rustc::hir::map::{self, DefCollector};
 use rustc::{ty, lint};
 use syntax::ast::{self, Name, Ident};
 use syntax::attr::{self, HasAttrs};
-use syntax::codemap::respan;
 use syntax::errors::DiagnosticBuilder;
 use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator};
 use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
@@ -137,15 +136,15 @@ impl<'a> base::Resolver for Resolver<'a> {
 
         impl<'a, 'b> Folder for EliminateCrateVar<'a, 'b> {
             fn fold_path(&mut self, mut path: ast::Path) -> ast::Path {
-                let ident = path.segments[0].identifier;
+                let ident = path.segments[0].ident;
                 if ident.name == keywords::DollarCrate.name() {
-                    path.segments[0].identifier.name = keywords::CrateRoot.name();
-                    let module = self.0.resolve_crate_root(ident.ctxt, true);
+                    path.segments[0].ident.name = keywords::CrateRoot.name();
+                    let module = self.0.resolve_crate_root(ident.span.ctxt(), true);
                     if !module.is_local() {
-                        let span = path.segments[0].span;
+                        let span = path.segments[0].ident.span;
                         path.segments.insert(1, match module.kind {
                             ModuleKind::Def(_, name) => ast::PathSegment::from_ident(
-                                ast::Ident::with_empty_ctxt(name), span
+                                ast::Ident::with_empty_ctxt(name).with_span_pos(span)
                             ),
                             _ => unreachable!(),
                         })
@@ -249,7 +248,7 @@ impl<'a> base::Resolver for Resolver<'a> {
                     if traits[j].segments.len() > 1 {
                         continue
                     }
-                    let trait_name = traits[j].segments[0].identifier.name;
+                    let trait_name = traits[j].segments[0].ident.name;
                     let legacy_name = Symbol::intern(&format!("derive_{}", trait_name));
                     if !self.global_macros.contains_key(&legacy_name) {
                         continue
@@ -268,7 +267,7 @@ impl<'a> base::Resolver for Resolver<'a> {
                                 if k > 0 {
                                     tokens.push(TokenTree::Token(path.span, Token::ModSep).into());
                                 }
-                                let tok = Token::from_ast_ident(segment.identifier);
+                                let tok = Token::from_ast_ident(segment.ident);
                                 tokens.push(TokenTree::Token(path.span, tok).into());
                             }
                         }
@@ -278,7 +277,7 @@ impl<'a> base::Resolver for Resolver<'a> {
                         }).into();
                     }
                     return Some(ast::Attribute {
-                        path: ast::Path::from_ident(span, Ident::with_empty_ctxt(legacy_name)),
+                        path: ast::Path::from_ident(Ident::new(legacy_name, span)),
                         tokens: TokenStream::empty(),
                         id: attr::mk_attr_id(),
                         style: ast::AttrStyle::Outer,
@@ -365,7 +364,7 @@ impl<'a> Resolver<'a> {
         }
 
         let attr_name = match path.segments.len() {
-            1 => path.segments[0].identifier.name,
+            1 => path.segments[0].ident.name,
             _ => return Err(determinacy),
         };
         for path in traits {
@@ -413,7 +412,7 @@ impl<'a> Resolver<'a> {
                                   kind: MacroKind, force: bool)
                                   -> Result<Def, Determinacy> {
         let ast::Path { ref segments, span } = *path;
-        let path: Vec<_> = segments.iter().map(|seg| respan(seg.span, seg.identifier)).collect();
+        let path: Vec<_> = segments.iter().map(|seg| seg.ident).collect();
         let invocation = self.invocations[&scope];
         let module = invocation.module.get();
         self.current_module = if module.is_trait() { module.parent.unwrap() } else { module };
@@ -447,19 +446,16 @@ impl<'a> Resolver<'a> {
                     Err(Determinacy::Determined)
                 },
             };
-            let path = path.iter().map(|p| p.node).collect::<Vec<_>>();
             self.current_module.nearest_item_scope().macro_resolutions.borrow_mut()
                 .push((path.into_boxed_slice(), span));
             return def;
         }
 
-        let legacy_resolution = self.resolve_legacy_scope(&invocation.legacy_scope,
-                                                          path[0].node,
-                                                          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 {
-            match self.resolve_lexical_macro_path_segment(path[0].node, MacroNS, false, span) {
+            match self.resolve_lexical_macro_path_segment(path[0], MacroNS, false, span) {
                 Ok(binding) => Ok(binding.binding().def_ignoring_ambiguity()),
                 Err(Determinacy::Undetermined) if !force => return Err(Determinacy::Undetermined),
                 Err(_) => {
@@ -470,7 +466,7 @@ impl<'a> Resolver<'a> {
         };
 
         self.current_module.nearest_item_scope().legacy_macro_resolutions.borrow_mut()
-            .push((scope, path[0].node, span, kind));
+            .push((scope, path[0], span, kind));
 
         result
     }
@@ -534,7 +530,7 @@ impl<'a> Resolver<'a> {
             }
 
             module = match module {
-                Some(module) => self.hygienic_lexical_parent(module, &mut ident.ctxt),
+                Some(module) => self.hygienic_lexical_parent(module, &mut ident.span),
                 None => return potential_illegal_shadower,
             }
         }
@@ -608,7 +604,6 @@ impl<'a> Resolver<'a> {
     pub fn finalize_current_module_macro_resolutions(&mut self) {
         let module = self.current_module;
         for &(ref path, span) in module.macro_resolutions.borrow().iter() {
-            let path = path.iter().map(|p| respan(span, *p)).collect::<Vec<_>>();
             match self.resolve_path(&path, Some(MacroNS), true, span) {
                 PathResult::NonModule(_) => {},
                 PathResult::Failed(span, msg, _) => {
@@ -684,8 +679,8 @@ impl<'a> Resolver<'a> {
                     false
                 }
             };
-            let ident = Ident::from_str(name);
-            self.lookup_typo_candidate(&vec![respan(span, ident)], MacroNS, is_macro, span)
+            let ident = Ident::new(Symbol::intern(name), span);
+            self.lookup_typo_candidate(&vec![ident], MacroNS, is_macro, span)
         });
 
         if let Some(suggestion) = suggestion {
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index 7036bdd0e2b..87738f7b79b 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -24,7 +24,7 @@ use rustc::hir::def::*;
 use rustc::session::DiagnosticMessageId;
 use rustc::util::nodemap::{FxHashMap, FxHashSet};
 
-use syntax::ast::{Ident, Name, SpannedIdent, NodeId};
+use syntax::ast::{Ident, Name, NodeId};
 use syntax::ext::base::Determinacy::{self, Determined, Undetermined};
 use syntax::ext::hygiene::Mark;
 use syntax::parse::token;
@@ -58,7 +58,7 @@ pub enum ImportDirectiveSubclass<'a> {
 pub struct ImportDirective<'a> {
     pub id: NodeId,
     pub parent: Module<'a>,
-    pub module_path: Vec<SpannedIdent>,
+    pub module_path: Vec<Ident>,
     pub imported_module: Cell<Option<Module<'a>>>, // the resolution of `module_path`
     pub subclass: ImportDirectiveSubclass<'a>,
     pub span: Span,
@@ -238,7 +238,7 @@ impl<'a> Resolver<'a> {
             }
             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()) {
+            match ident.span.glob_adjust(module.expansion, directive.span.ctxt().modern()) {
                 Some(Some(def)) => self.current_module = self.macro_def_scope(def),
                 Some(None) => {}
                 None => continue,
@@ -257,7 +257,7 @@ impl<'a> Resolver<'a> {
 
     // Add an import directive to the current module.
     pub fn add_import_directive(&mut self,
-                                module_path: Vec<SpannedIdent>,
+                                module_path: Vec<Ident>,
                                 subclass: ImportDirectiveSubclass<'a>,
                                 span: Span,
                                 id: NodeId,
@@ -398,7 +398,7 @@ impl<'a> Resolver<'a> {
         // Define `binding` in `module`s glob importers.
         for directive in module.glob_importers.borrow_mut().iter() {
             let mut ident = ident.modern();
-            let scope = match ident.ctxt.reverse_glob_adjust(module.expansion,
+            let scope = match ident.span.reverse_glob_adjust(module.expansion,
                                                              directive.span.ctxt().modern()) {
                 Some(Some(def)) => self.macro_def_scope(def),
                 Some(None) => directive.parent,
@@ -606,9 +606,9 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
 
         // FIXME: Last path segment is treated specially in import resolution, so extern crate
         // mode for absolute paths needs some special support for single-segment imports.
-        if module_path.len() == 1 && (module_path[0].node.name == keywords::CrateRoot.name() ||
-                                      module_path[0].node.name == keywords::Extern.name()) {
-            let is_extern = module_path[0].node.name == keywords::Extern.name() ||
+        if module_path.len() == 1 && (module_path[0].name == keywords::CrateRoot.name() ||
+                                      module_path[0].name == keywords::Extern.name()) {
+            let is_extern = module_path[0].name == keywords::Extern.name() ||
                             self.session.features_untracked().extern_absolute_paths;
             match directive.subclass {
                 GlobImport { .. } if is_extern => {
@@ -617,13 +617,13 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                 }
                 SingleImport { source, target, .. } => {
                     let crate_root = if source.name == keywords::Crate.name() &&
-                                        module_path[0].node.name != keywords::Extern.name() {
+                                        module_path[0].name != keywords::Extern.name() {
                         if target.name == keywords::Crate.name() {
                             return Some((directive.span,
                                          "crate root imports need to be explicitly named: \
                                           `use crate as name;`".to_string()));
                         } else {
-                            Some(self.resolve_crate_root(source.ctxt.modern(), false))
+                            Some(self.resolve_crate_root(source.span.ctxt().modern(), false))
                         }
                     } else if is_extern && !token::is_path_segment_keyword(source) {
                         let crate_id =
@@ -669,9 +669,9 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                 let (mut self_path, mut self_result) = (module_path.clone(), None);
                 let is_special = |ident| token::is_path_segment_keyword(ident) &&
                                          ident.name != keywords::CrateRoot.name();
-                if !self_path.is_empty() && !is_special(self_path[0].node) &&
-                   !(self_path.len() > 1 && is_special(self_path[1].node)) {
-                    self_path[0].node.name = keywords::SelfValue.name();
+                if !self_path.is_empty() && !is_special(self_path[0]) &&
+                   !(self_path.len() > 1 && is_special(self_path[1])) {
+                    self_path[0].name = keywords::SelfValue.name();
                     self_result = Some(self.resolve_path(&self_path, None, false, span));
                 }
                 return if let Some(PathResult::Module(..)) = self_result {
@@ -860,7 +860,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
             resolution.borrow().binding().map(|binding| (ident, binding))
         }).collect::<Vec<_>>();
         for ((mut ident, ns), binding) in bindings {
-            let scope = match ident.ctxt.reverse_glob_adjust(module.expansion,
+            let scope = match ident.span.reverse_glob_adjust(module.expansion,
                                                              directive.span.ctxt().modern()) {
                 Some(Some(def)) => self.macro_def_scope(def),
                 Some(None) => self.current_module,
@@ -957,7 +957,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                             let resolutions = imported_module.parent.expect("parent should exist")
                                 .resolutions.borrow();
                             let enum_path_segment_index = directive.module_path.len() - 1;
-                            let enum_ident = directive.module_path[enum_path_segment_index].node;
+                            let enum_ident = directive.module_path[enum_path_segment_index];
 
                             let enum_resolution = resolutions.get(&(enum_ident, TypeNS))
                                 .expect("resolution should exist");
@@ -1011,12 +1011,12 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
     }
 }
 
-fn import_path_to_string(names: &[SpannedIdent],
+fn import_path_to_string(names: &[Ident],
                          subclass: &ImportDirectiveSubclass,
                          span: Span) -> String {
     let pos = names.iter()
-        .position(|p| span == p.span && p.node.name != keywords::CrateRoot.name());
-    let global = !names.is_empty() && names[0].node.name == keywords::CrateRoot.name();
+        .position(|p| span == p.span && p.name != keywords::CrateRoot.name());
+    let global = !names.is_empty() && names[0].name == keywords::CrateRoot.name();
     if let Some(pos) = pos {
         let names = if global { &names[1..pos + 1] } else { &names[..pos + 1] };
         names_to_string(names)
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index 5e51797d720..ed8904b34a2 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -183,7 +183,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
         for (i, seg) in segments.iter().enumerate() {
             segs.push(seg.clone());
             let sub_path = ast::Path {
-                span: seg.span, // span for the last segment
+                span: seg.ident.span, // span for the last segment
                 segments: segs,
             };
             let qualname = if i == 0 && path.is_global() {
@@ -191,7 +191,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
             } else {
                 path_to_string(&sub_path)
             };
-            result.push((seg.span, qualname));
+            result.push((seg.ident.span, qualname));
             segs = sub_path.segments;
         }
 
@@ -351,14 +351,14 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
             collector.visit_pat(&arg.pat);
             let span_utils = self.span.clone();
 
-            for (id, i, sp, ..) in collector.collected_idents {
+            for (id, ident, ..) in collector.collected_idents {
                 let hir_id = self.tcx.hir.node_to_hir_id(id);
                 let typ = match self.save_ctxt.tables.node_id_to_type_opt(hir_id) {
                     Some(s) => s.to_string(),
                     None => continue,
                 };
-                let sub_span = span_utils.span_for_last_ident(sp);
-                if !self.span.filter_generated(sub_span, sp) {
+                let sub_span = span_utils.span_for_last_ident(ident.span);
+                if !self.span.filter_generated(sub_span, ident.span) {
                     let id = ::id_from_node_id(id, &self.save_ctxt);
                     let span = self.span_from_span(sub_span.expect("No span found for variable"));
 
@@ -371,8 +371,8 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
                             kind: DefKind::Local,
                             id,
                             span,
-                            name: i.to_string(),
-                            qualname: format!("{}::{}", qualname, i.to_string()),
+                            name: ident.to_string(),
+                            qualname: format!("{}::{}", qualname, ident.to_string()),
                             value: typ,
                             parent: None,
                             children: vec![],
@@ -447,7 +447,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
     ) {
         for param in &generics.params {
             if let ast::GenericParam::Type(ref ty_param) = *param {
-                let param_ss = ty_param.span;
+                let param_ss = ty_param.ident.span;
                 let name = escape(self.span.snippet(param_ss));
                 // Append $id to name to make sure each one is unique
                 let qualname = format!("{}::{}${}", prefix, name, id);
@@ -663,7 +663,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
         let access = access_from!(self.save_ctxt, item);
 
         for variant in &enum_definition.variants {
-            let name = variant.node.name.name.to_string();
+            let name = variant.node.ident.name.to_string();
             let mut qualname = enum_data.qualname.clone();
             qualname.push_str("::");
             qualname.push_str(&name);
@@ -1040,11 +1040,11 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
         }
 
         // process collected paths
-        for (id, i, sp, immut) in collector.collected_idents {
+        for (id, ident, immut) in collector.collected_idents {
             match self.save_ctxt.get_path_def(id) {
                 HirDef::Local(id) => {
                     let mut value = if immut == ast::Mutability::Immutable {
-                        self.span.snippet(sp).to_string()
+                        self.span.snippet(ident.span).to_string()
                     } else {
                         "<mutable>".to_string()
                     };
@@ -1057,10 +1057,10 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
                     value.push_str(": ");
                     value.push_str(&typ);
 
-                    if !self.span.filter_generated(Some(sp), sp) {
-                        let qualname = format!("{}${}", i.to_string(), id);
+                    if !self.span.filter_generated(Some(ident.span), ident.span) {
+                        let qualname = format!("{}${}", ident.to_string(), id);
                         let id = ::id_from_node_id(id, &self.save_ctxt);
-                        let span = self.span_from_span(sp);
+                        let span = self.span_from_span(ident.span);
 
                         self.dumper.dump_def(
                             &Access {
@@ -1071,7 +1071,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
                                 kind: DefKind::Local,
                                 id,
                                 span,
-                                name: i.to_string(),
+                                name: ident.to_string(),
                                 qualname,
                                 value: typ,
                                 parent: None,
@@ -1093,7 +1093,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
                 HirDef::TyAlias(..) |
                 HirDef::AssociatedTy(..) |
                 HirDef::SelfTy(..) => {
-                    self.dump_path_ref(id, &ast::Path::from_ident(sp, i));
+                    self.dump_path_ref(id, &ast::Path::from_ident(ident));
                 }
                 def => error!(
                     "unexpected definition kind when processing collected idents: {:?}",
@@ -1114,7 +1114,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
         collector.visit_pat(&p);
         self.visit_pat(&p);
 
-        for (id, i, sp, immut) in collector.collected_idents {
+        for (id, ident, immut) in collector.collected_idents {
             let mut value = match immut {
                 ast::Mutability::Immutable => value.to_string(),
                 _ => String::new(),
@@ -1134,10 +1134,10 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
 
             // Get the span only for the name of the variable (I hope the path
             // is only ever a variable name, but who knows?).
-            let sub_span = self.span.span_for_last_ident(sp);
+            let sub_span = self.span.span_for_last_ident(ident.span);
             // Rust uses the id of the pattern for var lookups, so we'll use it too.
-            if !self.span.filter_generated(sub_span, sp) {
-                let qualname = format!("{}${}", i.to_string(), id);
+            if !self.span.filter_generated(sub_span, ident.span) {
+                let qualname = format!("{}${}", ident.to_string(), id);
                 let id = ::id_from_node_id(id, &self.save_ctxt);
                 let span = self.span_from_span(sub_span.expect("No span found for variable"));
 
@@ -1150,7 +1150,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
                         kind: DefKind::Local,
                         id,
                         span,
-                        name: i.to_string(),
+                        name: ident.to_string(),
                         qualname,
                         value: typ,
                         parent: None,
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index fb4cb2afe5a..4f46fb3545b 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -297,7 +297,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 filter!(self.span_utils, sub_span, item.span, None);
                 let variants_str = def.variants
                     .iter()
-                    .map(|v| v.node.name.to_string())
+                    .map(|v| v.node.ident.to_string())
                     .collect::<Vec<_>>()
                     .join(", ");
                 let value = format!("{}::{{{}}}", name, variants_str);
@@ -554,7 +554,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 };
                 match self.tables.expr_ty_adjusted(&hir_node).sty {
                     ty::TyAdt(def, _) if !def.is_enum() => {
-                        let f = def.non_enum_variant().field_named(ident.node.name);
+                        let f = def.non_enum_variant().field_named(ident.name);
                         let sub_span = self.span_utils.span_for_last_ident(expr.span);
                         filter!(self.span_utils, sub_span, expr.span, None);
                         let span = self.span_from_span(sub_span.unwrap());
@@ -603,7 +603,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     ty::ImplContainer(_) => (Some(method_id), None),
                     ty::TraitContainer(_) => (None, Some(method_id)),
                 };
-                let sub_span = seg.span;
+                let sub_span = seg.ident.span;
                 filter!(self.span_utils, Some(sub_span), expr.span, None);
                 let span = self.span_from_span(sub_span);
                 Some(Data::RefData(Ref {
@@ -707,7 +707,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
 
         let def = self.get_path_def(id);
         let last_seg = &path.segments[path.segments.len() - 1];
-        let sub_span = last_seg.span;
+        let sub_span = last_seg.ident.span;
         filter!(self.span_utils, Some(sub_span), path.span, None);
         match def {
             HirDef::Upvar(id, ..) | HirDef::Local(id) => {
@@ -817,7 +817,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         field_ref: &ast::Field,
         variant: &ty::VariantDef,
     ) -> Option<Ref> {
-        let f = variant.find_field_named(field_ref.ident.node.name)?;
+        let f = variant.find_field_named(field_ref.ident.name)?;
         // We don't really need a sub-span here, but no harm done
         let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span);
         filter!(self.span_utils, sub_span, field_ref.ident.span, None);
@@ -961,7 +961,7 @@ fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
 // variables (idents) from patterns.
 struct PathCollector<'l> {
     collected_paths: Vec<(NodeId, &'l ast::Path)>,
-    collected_idents: Vec<(NodeId, ast::Ident, Span, ast::Mutability)>,
+    collected_idents: Vec<(NodeId, ast::Ident, ast::Mutability)>,
 }
 
 impl<'l> PathCollector<'l> {
@@ -982,12 +982,12 @@ impl<'l, 'a: 'l> Visitor<'a> for PathCollector<'l> {
             PatKind::TupleStruct(ref path, ..) | PatKind::Path(_, ref path) => {
                 self.collected_paths.push((p.id, path));
             }
-            PatKind::Ident(bm, ref path1, _) => {
+            PatKind::Ident(bm, ident, _) => {
                 debug!(
                     "PathCollector, visit ident in pat {}: {:?} {:?}",
-                    path1.node,
+                    ident,
                     p.span,
-                    path1.span
+                    ident.span
                 );
                 let immut = match bm {
                     // Even if the ref is mut, you can't change the ref, only
@@ -997,7 +997,7 @@ impl<'l, 'a: 'l> Visitor<'a> for PathCollector<'l> {
                     ast::BindingMode::ByValue(mt) => mt,
                 };
                 self.collected_idents
-                    .push((p.id, path1.node, path1.span, immut));
+                    .push((p.id, ident, immut));
             }
             _ => {}
         }
diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs
index fd4d3e76386..a9df898efb6 100644
--- a/src/librustc_save_analysis/sig.rs
+++ b/src/librustc_save_analysis/sig.rs
@@ -671,7 +671,7 @@ impl Sig for ast::StructField {
     fn make(&self, offset: usize, _parent_id: Option<NodeId>, scx: &SaveContext) -> Result {
         let mut text = String::new();
         let mut defs = None;
-        if let Some(ref ident) = self.ident {
+        if let Some(ident) = self.ident {
             text.push_str(&ident.to_string());
             defs = Some(SigElement {
                 id: id_from_node_id(self.id, scx),
@@ -692,7 +692,7 @@ impl Sig for ast::StructField {
 
 impl Sig for ast::Variant_ {
     fn make(&self, offset: usize, _parent_id: Option<NodeId>, scx: &SaveContext) -> Result {
-        let mut text = self.name.to_string();
+        let mut text = self.ident.to_string();
         match self.data {
             ast::VariantData::Struct(ref fields, id) => {
                 let name_def = SigElement {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 27e0f6ad6dd..6d85e949d6b 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3213,10 +3213,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     if !tuple_like { continue }
 
                     debug!("tuple struct named {:?}",  base_t);
-                    let ident = ast::Ident {
-                        name: Symbol::intern(&idx.node.to_string()),
-                        ctxt: idx.span.ctxt().modern(),
-                    };
+                    let ident =
+                        ast::Ident::new(Symbol::intern(&idx.node.to_string()), idx.span.modern());
                     let (ident, def_scope) =
                         self.tcx.adjust_ident(ident, base_def.did, self.body_id);
                     let fields = &base_def.non_enum_variant().fields;
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index fe4f785aa91..a87e1df5efc 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -171,7 +171,7 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> {
             let mut segments = path.segments.into_vec();
             let last = segments.pop().unwrap();
 
-            let real_name = name.as_ref().map(|n| Symbol::from(n.as_str()));
+            let real_name = name.map(|name| Symbol::intern(&name));
 
             segments.push(hir::PathSegment::new(
                 real_name.unwrap_or(last.name),
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index c228f54217d..7f89b3e6b3a 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -67,7 +67,7 @@ impl Cfg {
     /// If the content is not properly formatted, it will return an error indicating what and where
     /// the error is.
     pub fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
-        let name = cfg.name();
+        let name = cfg.ident.name;
         match cfg.node {
             MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
             MetaItemKind::NameValue(ref lit) => match lit.node {
@@ -562,14 +562,14 @@ mod test {
     fn test_parse_ok() {
         with_globals(|| {
             let mi = MetaItem {
-                name: Symbol::intern("all"),
+                ident: Ident::from_str("all"),
                 node: MetaItemKind::Word,
                 span: DUMMY_SP,
             };
             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
 
             let mi = MetaItem {
-                name: Symbol::intern("all"),
+                ident: Ident::from_str("all"),
                 node: MetaItemKind::NameValue(dummy_spanned(LitKind::Str(
                     Symbol::intern("done"),
                     StrStyle::Cooked,
@@ -579,15 +579,15 @@ mod test {
             assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done")));
 
             let mi = MetaItem {
-                name: Symbol::intern("all"),
+                ident: Ident::from_str("all"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("a"),
+                        ident: Ident::from_str("a"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("b"),
+                        ident: Ident::from_str("b"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
@@ -597,15 +597,15 @@ mod test {
             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b")));
 
             let mi = MetaItem {
-                name: Symbol::intern("any"),
+                ident: Ident::from_str("any"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("a"),
+                        ident: Ident::from_str("a"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("b"),
+                        ident: Ident::from_str("b"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
@@ -615,10 +615,10 @@ mod test {
             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b")));
 
             let mi = MetaItem {
-                name: Symbol::intern("not"),
+                ident: Ident::from_str("not"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("a"),
+                        ident: Ident::from_str("a"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
@@ -628,26 +628,26 @@ mod test {
             assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a")));
 
             let mi = MetaItem {
-                name: Symbol::intern("not"),
+                ident: Ident::from_str("not"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("any"),
+                        ident: Ident::from_str("any"),
                         node: MetaItemKind::List(vec![
                             dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                                name: Symbol::intern("a"),
+                                ident: Ident::from_str("a"),
                                 node: MetaItemKind::Word,
                                 span: DUMMY_SP,
                             })),
                             dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                                name: Symbol::intern("all"),
+                                ident: Ident::from_str("all"),
                                 node: MetaItemKind::List(vec![
                                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                                        name: Symbol::intern("b"),
+                                        ident: Ident::from_str("b"),
                                         node: MetaItemKind::Word,
                                         span: DUMMY_SP,
                                     })),
                                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                                        name: Symbol::intern("c"),
+                                        ident: Ident::from_str("c"),
                                         node: MetaItemKind::Word,
                                         span: DUMMY_SP,
                                     })),
@@ -663,20 +663,20 @@ mod test {
             assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c")))));
 
             let mi = MetaItem {
-                name: Symbol::intern("all"),
+                ident: Ident::from_str("all"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("a"),
+                        ident: Ident::from_str("a"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("b"),
+                        ident: Ident::from_str("b"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("c"),
+                        ident: Ident::from_str("c"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
@@ -691,22 +691,22 @@ mod test {
     fn test_parse_err() {
         with_globals(|| {
             let mi = MetaItem {
-                name: Symbol::intern("foo"),
+                ident: Ident::from_str("foo"),
                 node: MetaItemKind::NameValue(dummy_spanned(LitKind::Bool(false))),
                 span: DUMMY_SP,
             };
             assert!(Cfg::parse(&mi).is_err());
 
             let mi = MetaItem {
-                name: Symbol::intern("not"),
+                ident: Ident::from_str("not"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("a"),
+                        ident: Ident::from_str("a"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("b"),
+                        ident: Ident::from_str("b"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
@@ -716,17 +716,17 @@ mod test {
             assert!(Cfg::parse(&mi).is_err());
 
             let mi = MetaItem {
-                name: Symbol::intern("not"),
+                ident: Ident::from_str("not"),
                 node: MetaItemKind::List(vec![]),
                 span: DUMMY_SP,
             };
             assert!(Cfg::parse(&mi).is_err());
 
             let mi = MetaItem {
-                name: Symbol::intern("foo"),
+                ident: Ident::from_str("foo"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("a"),
+                        ident: Ident::from_str("a"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
@@ -736,15 +736,15 @@ mod test {
             assert!(Cfg::parse(&mi).is_err());
 
             let mi = MetaItem {
-                name: Symbol::intern("all"),
+                ident: Ident::from_str("all"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("foo"),
+                        ident: Ident::from_str("foo"),
                         node: MetaItemKind::List(vec![]),
                         span: DUMMY_SP,
                     })),
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("b"),
+                        ident: Ident::from_str("b"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
@@ -754,15 +754,15 @@ mod test {
             assert!(Cfg::parse(&mi).is_err());
 
             let mi = MetaItem {
-                name: Symbol::intern("any"),
+                ident: Ident::from_str("any"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("a"),
+                        ident: Ident::from_str("a"),
                         node: MetaItemKind::Word,
                         span: DUMMY_SP,
                     })),
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("foo"),
+                        ident: Ident::from_str("foo"),
                         node: MetaItemKind::List(vec![]),
                         span: DUMMY_SP,
                     })),
@@ -772,10 +772,10 @@ mod test {
             assert!(Cfg::parse(&mi).is_err());
 
             let mi = MetaItem {
-                name: Symbol::intern("not"),
+                ident: Ident::from_str("not"),
                 node: MetaItemKind::List(vec![
                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
-                        name: Symbol::intern("foo"),
+                        ident: Ident::from_str("foo"),
                         node: MetaItemKind::List(vec![]),
                         span: DUMMY_SP,
                     })),
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 6ada4ccfbdd..b57c9589afa 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -21,9 +21,9 @@ pub use self::Visibility::*;
 
 use syntax;
 use syntax::abi::Abi;
-use syntax::ast::{self, AttrStyle};
+use syntax::ast::{self, AttrStyle, Ident};
 use syntax::attr;
-use syntax::codemap::Spanned;
+use syntax::codemap::{dummy_spanned, Spanned};
 use syntax::feature_gate::UnstableFeatures;
 use syntax::ptr::P;
 use syntax::symbol::keywords;
@@ -840,7 +840,8 @@ impl Attributes {
         for attr in attrs.lists("target_feature") {
             if attr.check_name("enable") {
                 if let Some(feat) = attr.value_str() {
-                    let meta = attr::mk_name_value_item_str("target_feature".into(), feat);
+                    let meta = attr::mk_name_value_item_str(Ident::from_str("target_feature"),
+                                                            dummy_spanned(feat));
                     if let Ok(feat_cfg) = Cfg::parse(&meta) {
                         cfg &= feat_cfg;
                     }
@@ -1146,16 +1147,8 @@ fn resolve(cx: &DocContext, path_str: &str, is_val: bool) -> Result<(Def, Option
 fn macro_resolve(cx: &DocContext, path_str: &str) -> Option<Def> {
     use syntax::ext::base::{MacroKind, SyntaxExtension};
     use syntax::ext::hygiene::Mark;
-    let segment = ast::PathSegment {
-        identifier: ast::Ident::from_str(path_str),
-        span: DUMMY_SP,
-        parameters: None,
-    };
-    let path = ast::Path {
-        span: DUMMY_SP,
-        segments: vec![segment],
-    };
-
+    let segment = ast::PathSegment::from_ident(Ident::from_str(path_str));
+    let path = ast::Path { segments: vec![segment], span: DUMMY_SP };
     let mut resolver = cx.resolver.borrow_mut();
     let mark = Mark::root();
     let res = resolver
@@ -1166,7 +1159,7 @@ fn macro_resolve(cx: &DocContext, path_str: &str) -> Option<Def> {
         } else {
             None
         }
-    } else if let Some(def) = resolver.all_macros.get(&path_str.into()) {
+    } else if let Some(def) = resolver.all_macros.get(&Symbol::intern(path_str)) {
         Some(*def)
     } else {
         None
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 8e6dcf8caf4..1c9ea261841 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -2966,7 +2966,7 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
 }
 
 fn render_attribute(attr: &ast::MetaItem) -> Option<String> {
-    let name = attr.name();
+    let name = attr.ident.name;
 
     if attr.is_word() {
         Some(format!("{}", name))
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 9d0c85a34f9..e7900af7f12 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -36,7 +36,6 @@ use std::u32;
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)]
 pub struct Label {
     pub ident: Ident,
-    pub span: Span,
 }
 
 impl fmt::Debug for Label {
@@ -48,7 +47,6 @@ impl fmt::Debug for Label {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)]
 pub struct Lifetime {
     pub id: NodeId,
-    pub span: Span,
     pub ident: Ident,
 }
 
@@ -82,7 +80,7 @@ pub struct Path {
 
 impl<'a> PartialEq<&'a str> for Path {
     fn eq(&self, string: &&'a str) -> bool {
-        self.segments.len() == 1 && self.segments[0].identifier.name == *string
+        self.segments.len() == 1 && self.segments[0].ident.name == *string
     }
 }
 
@@ -101,17 +99,14 @@ impl fmt::Display for Path {
 impl Path {
     // convert a span and an identifier to the corresponding
     // 1-segment path
-    pub fn from_ident(s: Span, identifier: Ident) -> Path {
-        Path {
-            span: s,
-            segments: vec![PathSegment::from_ident(identifier, s)],
-        }
+    pub fn from_ident(ident: Ident) -> Path {
+        Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span }
     }
 
     // Make a "crate root" segment for this path unless it already has it
     // or starts with something like `self`/`super`/`$crate`/etc.
     pub fn make_root(&self) -> Option<PathSegment> {
-        if let Some(ident) = self.segments.get(0).map(|seg| seg.identifier) {
+        if let Some(ident) = self.segments.get(0).map(|seg| seg.ident) {
             if ::parse::token::is_path_segment_keyword(ident) &&
                ident.name != keywords::Crate.name() {
                 return None;
@@ -121,7 +116,7 @@ impl Path {
     }
 
     pub fn is_global(&self) -> bool {
-        !self.segments.is_empty() && self.segments[0].identifier.name == keywords::CrateRoot.name()
+        !self.segments.is_empty() && self.segments[0].ident.name == keywords::CrateRoot.name()
     }
 }
 
@@ -131,9 +126,7 @@ impl Path {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct PathSegment {
     /// The identifier portion of this path segment.
-    pub identifier: Ident,
-    /// Span of the segment identifier.
-    pub span: Span,
+    pub ident: Ident,
 
     /// Type/lifetime parameters attached to this path. They come in
     /// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`.
@@ -145,15 +138,11 @@ pub struct PathSegment {
 }
 
 impl PathSegment {
-    pub fn from_ident(ident: Ident, span: Span) -> Self {
-        PathSegment { identifier: ident, span: span, parameters: None }
+    pub fn from_ident(ident: Ident) -> Self {
+        PathSegment { ident, parameters: None }
     }
     pub fn crate_root(span: Span) -> Self {
-        PathSegment {
-            identifier: Ident { ctxt: span.ctxt(), ..keywords::CrateRoot.ident() },
-            span,
-            parameters: None,
-        }
+        PathSegment::from_ident(Ident::new(keywords::CrateRoot.name(), span))
     }
 }
 
@@ -297,7 +286,7 @@ impl TyParamBound {
     pub fn span(&self) -> Span {
         match self {
             &TraitTyParamBound(ref t, ..) => t.span,
-            &RegionTyParamBound(ref l) => l.span,
+            &RegionTyParamBound(ref l) => l.ident.span,
         }
     }
 }
@@ -319,7 +308,6 @@ pub struct TyParam {
     pub id: NodeId,
     pub bounds: TyParamBounds,
     pub default: Option<P<Ty>>,
-    pub span: Span,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -370,7 +358,7 @@ impl Generics {
         for param in &self.params {
             if let GenericParam::Type(ref t) = *param {
                 if t.ident.name == name {
-                    return Some(t.span);
+                    return Some(t.ident.span);
                 }
             }
         }
@@ -489,7 +477,7 @@ pub enum NestedMetaItemKind {
 /// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct MetaItem {
-    pub name: Name,
+    pub ident: Ident,
     pub node: MetaItemKind,
     pub span: Span,
 }
@@ -545,7 +533,7 @@ impl Pat {
         let node = match &self.node {
             PatKind::Wild => TyKind::Infer,
             PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None) =>
-                TyKind::Path(None, Path::from_ident(ident.span, ident.node)),
+                TyKind::Path(None, Path::from_ident(*ident)),
             PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()),
             PatKind::Mac(mac) => TyKind::Mac(mac.clone()),
             PatKind::Ref(pat, mutbl) =>
@@ -642,7 +630,7 @@ pub enum PatKind {
     /// or a unit struct/variant pattern, or a const pattern (in the last two cases the third
     /// field must be `None`). Disambiguation cannot be done with parser alone, so it happens
     /// during name resolution.
-    Ident(BindingMode, SpannedIdent, Option<P<Pat>>),
+    Ident(BindingMode, Ident, Option<P<Pat>>),
 
     /// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`.
     /// The `bool` is `true` in the presence of a `..`.
@@ -914,15 +902,13 @@ pub struct Arm {
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Field {
-    pub ident: SpannedIdent,
+    pub ident: Ident,
     pub expr: P<Expr>,
     pub span: Span,
     pub is_shorthand: bool,
     pub attrs: ThinVec<Attribute>,
 }
 
-pub type SpannedIdent = Spanned<Ident>;
-
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
 pub enum BlockCheckMode {
     Default,
@@ -1148,7 +1134,7 @@ pub enum ExprKind {
     /// For example, `a += 1`.
     AssignOp(BinOp, P<Expr>, P<Expr>),
     /// Access of a named struct field (`obj.foo`)
-    Field(P<Expr>, SpannedIdent),
+    Field(P<Expr>, Ident),
     /// Access of an unnamed field of a struct or tuple-struct
     ///
     /// For example, `foo.0`.
@@ -1693,7 +1679,7 @@ pub type ExplicitSelf = Spanned<SelfKind>;
 impl Arg {
     pub fn to_self(&self) -> Option<ExplicitSelf> {
         if let PatKind::Ident(BindingMode::ByValue(mutbl), ident, _) = self.pat.node {
-            if ident.node.name == keywords::SelfValue.name() {
+            if ident.name == keywords::SelfValue.name() {
                 return match self.ty.node {
                     TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
                     TyKind::Rptr(lt, MutTy{ref ty, mutbl}) if ty.node == TyKind::ImplicitSelf => {
@@ -1709,13 +1695,13 @@ impl Arg {
 
     pub fn is_self(&self) -> bool {
         if let PatKind::Ident(_, ident, _) = self.pat.node {
-            ident.node.name == keywords::SelfValue.name()
+            ident.name == keywords::SelfValue.name()
         } else {
             false
         }
     }
 
-    pub fn from_self(eself: ExplicitSelf, eself_ident: SpannedIdent) -> Arg {
+    pub fn from_self(eself: ExplicitSelf, eself_ident: Ident) -> Arg {
         let span = eself.span.to(eself_ident.span);
         let infer_ty = P(Ty {
             id: DUMMY_NODE_ID,
@@ -1872,7 +1858,7 @@ pub struct EnumDef {
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Variant_ {
-    pub name: Ident,
+    pub ident: Ident,
     pub attrs: Vec<Attribute>,
     pub data: VariantData,
     /// Explicit discriminant, e.g. `Foo = 1`
@@ -1906,7 +1892,7 @@ impl UseTree {
         match self.kind {
             UseTreeKind::Simple(Some(rename)) => rename,
             UseTreeKind::Simple(None) =>
-                self.prefix.segments.last().expect("empty prefix in a simple import").identifier,
+                self.prefix.segments.last().expect("empty prefix in a simple import").ident,
             _ => panic!("`UseTree::ident` can only be used on a simple import"),
         }
     }
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 5954b9eb274..2812e1238e9 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -19,7 +19,7 @@ use ast::{AttrId, Attribute, Name, Ident};
 use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
 use ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind};
 use codemap::{Spanned, respan, dummy_spanned};
-use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::Span;
 use errors::Handler;
 use feature_gate::{Features, GatedCfg};
 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
@@ -137,7 +137,7 @@ impl NestedMetaItem {
     /// Returns the name of the meta item, e.g. `foo` in `#[foo]`,
     /// `#[foo="bar"]` and `#[foo(bar)]`, if self is a MetaItem
     pub fn name(&self) -> Option<Name> {
-        self.meta_item().and_then(|meta_item| Some(meta_item.name()))
+        self.meta_item().and_then(|meta_item| Some(meta_item.ident.name))
     }
 
     /// Gets the string value if self is a MetaItem and the MetaItem is a
@@ -154,7 +154,7 @@ impl NestedMetaItem {
                     if meta_item_list.len() == 1 {
                         let nested_item = &meta_item_list[0];
                         if nested_item.is_literal() {
-                            Some((meta_item.name(), nested_item.literal().unwrap()))
+                            Some((meta_item.ident.name, nested_item.literal().unwrap()))
                         } else {
                             None
                         }
@@ -215,7 +215,7 @@ impl Attribute {
 
     pub fn name(&self) -> Option<Name> {
         match self.path.segments.len() {
-            1 => Some(self.path.segments[0].identifier.name),
+            1 => Some(self.path.segments[0].ident.name),
             _ => None,
         }
     }
@@ -250,10 +250,6 @@ impl Attribute {
 }
 
 impl MetaItem {
-    pub fn name(&self) -> Name {
-        self.name
-    }
-
     pub fn value_str(&self) -> Option<Symbol> {
         match self.node {
             MetaItemKind::NameValue(ref v) => {
@@ -283,7 +279,7 @@ impl MetaItem {
     pub fn span(&self) -> Span { self.span }
 
     pub fn check_name(&self, name: &str) -> bool {
-        self.name() == name
+        self.ident.name == name
     }
 
     pub fn is_value_str(&self) -> bool {
@@ -300,8 +296,8 @@ impl Attribute {
     pub fn meta(&self) -> Option<MetaItem> {
         let mut tokens = self.tokens.trees().peekable();
         Some(MetaItem {
-            name: match self.path.segments.len() {
-                1 => self.path.segments[0].identifier.name,
+            ident: match self.path.segments.len() {
+                1 => self.path.segments[0].ident,
                 _ => return None,
             },
             node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) {
@@ -353,7 +349,7 @@ impl Attribute {
         }
 
         Ok(MetaItem {
-            name: self.path.segments.last().unwrap().identifier.name,
+            ident: self.path.segments.last().unwrap().ident,
             node: self.parse(sess, |parser| parser.parse_meta_item_kind())?,
             span: self.span,
         })
@@ -368,8 +364,8 @@ impl Attribute {
         if self.is_sugared_doc {
             let comment = self.value_str().unwrap();
             let meta = mk_name_value_item_str(
-                Symbol::intern("doc"),
-                Symbol::intern(&strip_doc_comment_decoration(&comment.as_str())));
+                Ident::from_str("doc"),
+                dummy_spanned(Symbol::intern(&strip_doc_comment_decoration(&comment.as_str()))));
             let mut attr = if self.style == ast::AttrStyle::Outer {
                 mk_attr_outer(self.span, self.id, meta)
             } else {
@@ -385,37 +381,24 @@ impl Attribute {
 
 /* Constructors */
 
-pub fn mk_name_value_item_str(name: Name, value: Symbol) -> MetaItem {
-    let value_lit = dummy_spanned(LitKind::Str(value, ast::StrStyle::Cooked));
-    mk_spanned_name_value_item(DUMMY_SP, name, value_lit)
+pub fn mk_name_value_item_str(ident: Ident, value: Spanned<Symbol>) -> MetaItem {
+    let value = respan(value.span, LitKind::Str(value.node, ast::StrStyle::Cooked));
+    mk_name_value_item(ident.span.to(value.span), ident, value)
 }
 
-pub fn mk_name_value_item(name: Name, value: ast::Lit) -> MetaItem {
-    mk_spanned_name_value_item(DUMMY_SP, name, value)
+pub fn mk_name_value_item(span: Span, ident: Ident, value: ast::Lit) -> MetaItem {
+    MetaItem { ident, span, node: MetaItemKind::NameValue(value) }
 }
 
-pub fn mk_list_item(name: Name, items: Vec<NestedMetaItem>) -> MetaItem {
-    mk_spanned_list_item(DUMMY_SP, name, items)
+pub fn mk_list_item(span: Span, ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem {
+    MetaItem { ident, span, node: MetaItemKind::List(items) }
 }
 
-pub fn mk_list_word_item(name: Name) -> ast::NestedMetaItem {
-    dummy_spanned(NestedMetaItemKind::MetaItem(mk_spanned_word_item(DUMMY_SP, name)))
+pub fn mk_word_item(ident: Ident) -> MetaItem {
+    MetaItem { ident, span: ident.span, node: MetaItemKind::Word }
 }
-
-pub fn mk_word_item(name: Name) -> MetaItem {
-    mk_spanned_word_item(DUMMY_SP, name)
-}
-
-pub fn mk_spanned_name_value_item(sp: Span, name: Name, value: ast::Lit) -> MetaItem {
-    MetaItem { span: sp, name: name, node: MetaItemKind::NameValue(value) }
-}
-
-pub fn mk_spanned_list_item(sp: Span, name: Name, items: Vec<NestedMetaItem>) -> MetaItem {
-    MetaItem { span: sp, name: name, node: MetaItemKind::List(items) }
-}
-
-pub fn mk_spanned_word_item(sp: Span, name: Name) -> MetaItem {
-    MetaItem { span: sp, name: name, node: MetaItemKind::Word }
+pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
+    respan(ident.span, NestedMetaItemKind::MetaItem(mk_word_item(ident)))
 }
 
 pub fn mk_attr_id() -> AttrId {
@@ -439,7 +422,7 @@ pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute
     Attribute {
         id,
         style: ast::AttrStyle::Inner,
-        path: ast::Path::from_ident(item.span, ast::Ident::with_empty_ctxt(item.name)),
+        path: ast::Path::from_ident(item.ident),
         tokens: item.node.tokens(item.span),
         is_sugared_doc: false,
         span: sp,
@@ -457,7 +440,7 @@ pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute
     Attribute {
         id,
         style: ast::AttrStyle::Outer,
-        path: ast::Path::from_ident(item.span, ast::Ident::with_empty_ctxt(item.name)),
+        path: ast::Path::from_ident(item.ident),
         tokens: item.node.tokens(item.span),
         is_sugared_doc: false,
         span: sp,
@@ -470,7 +453,7 @@ pub fn mk_sugared_doc_attr(id: AttrId, text: Symbol, span: Span) -> Attribute {
     Attribute {
         id,
         style,
-        path: ast::Path::from_ident(span, ast::Ident::from_str("doc")),
+        path: ast::Path::from_ident(Ident::from_str("doc").with_span_pos(span)),
         tokens: MetaItemKind::NameValue(lit).tokens(span),
         is_sugared_doc: true,
         span,
@@ -506,7 +489,7 @@ pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
         item.check_name("feature") &&
         item.meta_item_list().map(|list| {
             list.iter().any(|mi| {
-                mi.word().map(|w| w.name() == feature_name)
+                mi.word().map(|w| w.ident.name == feature_name)
                          .unwrap_or(false)
             })
         }).unwrap_or(false)
@@ -579,7 +562,7 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
         if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
             gated_cfg.check_and_emit(sess, feats);
         }
-        sess.config.contains(&(cfg.name(), cfg.value_str()))
+        sess.config.contains(&(cfg.ident.name, cfg.value_str()))
     })
 }
 
@@ -600,7 +583,7 @@ pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
 
             // The unwraps below may look dangerous, but we've already asserted
             // that they won't fail with the loop above.
-            match &*cfg.name.as_str() {
+            match &*cfg.ident.name.as_str() {
                 "any" => mis.iter().any(|mi| {
                     eval_condition(mi.meta_item().unwrap(), sess, eval)
                 }),
@@ -693,7 +676,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
             let meta = meta.as_ref().unwrap();
             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
                 if item.is_some() {
-                    handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name()));
+                    handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.ident.name));
                     return false
                 }
                 if let Some(v) = meta.value_str() {
@@ -712,14 +695,14 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                     )+
                     for meta in metas {
                         if let Some(mi) = meta.meta_item() {
-                            match &*mi.name().as_str() {
+                            match &*mi.ident.name.as_str() {
                                 $(
                                     stringify!($name)
                                         => if !get(mi, &mut $name) { continue 'outer },
                                 )+
                                 _ => {
                                     handle_errors(diagnostic, mi.span,
-                                                  AttrError::UnknownMetaItem(mi.name()));
+                                                  AttrError::UnknownMetaItem(mi.ident.name));
                                     continue 'outer
                                 }
                             }
@@ -731,7 +714,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                 }
             }
 
-            match &*meta.name.as_str() {
+            match &*meta.ident.name.as_str() {
                 "rustc_deprecated" => {
                     if rustc_depr.is_some() {
                         span_err!(diagnostic, item_sp, E0540,
@@ -786,13 +769,13 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                     let mut issue = None;
                     for meta in metas {
                         if let Some(mi) = meta.meta_item() {
-                            match &*mi.name().as_str() {
+                            match &*mi.ident.name.as_str() {
                                 "feature" => if !get(mi, &mut feature) { continue 'outer },
                                 "reason" => if !get(mi, &mut reason) { continue 'outer },
                                 "issue" => if !get(mi, &mut issue) { continue 'outer },
                                 _ => {
                                     handle_errors(diagnostic, meta.span,
-                                                  AttrError::UnknownMetaItem(mi.name()));
+                                                  AttrError::UnknownMetaItem(mi.ident.name));
                                     continue 'outer
                                 }
                             }
@@ -842,12 +825,12 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                     let mut since = None;
                     for meta in metas {
                         if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
-                            match &*mi.name().as_str() {
+                            match &*mi.ident.name.as_str() {
                                 "feature" => if !get(mi, &mut feature) { continue 'outer },
                                 "since" => if !get(mi, &mut since) { continue 'outer },
                                 _ => {
                                     handle_errors(diagnostic, meta.span,
-                                                  AttrError::UnknownMetaItem(mi.name()));
+                                                  AttrError::UnknownMetaItem(mi.ident.name));
                                     continue 'outer
                                 }
                             }
@@ -934,7 +917,7 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
         depr = if let Some(metas) = attr.meta_item_list() {
             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
                 if item.is_some() {
-                    handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name()));
+                    handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.ident.name));
                     return false
                 }
                 if let Some(v) = meta.value_str() {
@@ -950,12 +933,12 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
             let mut note = None;
             for meta in metas {
                 if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
-                    match &*mi.name().as_str() {
+                    match &*mi.ident.name.as_str() {
                         "since" => if !get(mi, &mut since) { continue 'outer },
                         "note" => if !get(mi, &mut note) { continue 'outer },
                         _ => {
                             handle_errors(diagnostic, meta.span,
-                                          AttrError::UnknownMetaItem(mi.name()));
+                                          AttrError::UnknownMetaItem(mi.ident.name));
                             continue 'outer
                         }
                     }
@@ -1007,7 +990,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
 
                 let mut recognised = false;
                 if let Some(mi) = item.word() {
-                    let word = &*mi.name().as_str();
+                    let word = &*mi.ident.name.as_str();
                     let hint = match word {
                         "C" => Some(ReprC),
                         "packed" => Some(ReprPacked),
@@ -1106,18 +1089,17 @@ impl IntType {
 
 impl MetaItem {
     fn tokens(&self) -> TokenStream {
-        let ident = TokenTree::Token(self.span,
-                                     Token::from_ast_ident(Ident::with_empty_ctxt(self.name)));
+        let ident = TokenTree::Token(self.span, Token::from_ast_ident(self.ident));
         TokenStream::concat(vec![ident.into(), self.node.tokens(self.span)])
     }
 
     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
         where I: Iterator<Item = TokenTree>,
     {
-        let (span, name) = match tokens.next() {
-            Some(TokenTree::Token(span, Token::Ident(ident, _))) => (span, ident.name),
+        let (span, ident) = match tokens.next() {
+            Some(TokenTree::Token(span, Token::Ident(ident, _))) => (span, ident),
             Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => match nt.0 {
-                token::Nonterminal::NtIdent(ident, _) => (ident.span, ident.node.name),
+                token::Nonterminal::NtIdent(ident, _) => (ident.span, ident),
                 token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()),
                 _ => return None,
             },
@@ -1130,7 +1112,7 @@ impl MetaItem {
             MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(span.hi()),
             _ => span.hi(),
         };
-        Some(MetaItem { name, node, span: span.with_hi(hi) })
+        Some(MetaItem { ident, node, span: span.with_hi(hi) })
     }
 }
 
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index c0855d470c8..36911683a0e 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -204,7 +204,7 @@ impl<'a> StripUnconfigured<'a> {
                     self.configure(v).map(|v| {
                         Spanned {
                             node: ast::Variant_ {
-                                name: v.node.name,
+                                ident: v.node.ident,
                                 attrs: v.node.attrs,
                                 data: self.configure_variant_data(v.node.data),
                                 disr_expr: v.node.disr_expr,
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 5a735be55c0..a09bea25a24 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -251,7 +251,7 @@ impl<F> TTMacroExpander for F
                 if let tokenstream::TokenTree::Token(_, token::Interpolated(ref nt)) = tt {
                     if let token::NtIdent(ident, is_raw) = nt.0 {
                         return tokenstream::TokenTree::Token(ident.span,
-                                                             token::Ident(ident.node, is_raw));
+                                                             token::Ident(ident, is_raw));
                     }
                 }
                 fold::noop_fold_tt(tt, self)
@@ -876,8 +876,8 @@ impl<'a> ExtCtxt<'a> {
         ast::Ident::from_str(st)
     }
     pub fn std_path(&self, components: &[&str]) -> Vec<ast::Ident> {
-        let def_site = SyntaxContext::empty().apply_mark(self.current_expansion.mark);
-        iter::once(Ident { ctxt: def_site, ..keywords::DollarCrate.ident() })
+        let def_site = DUMMY_SP.apply_mark(self.current_expansion.mark);
+        iter::once(Ident::new(keywords::DollarCrate.name(), def_site))
             .chain(components.iter().map(|s| self.ident_of(s)))
             .collect()
     }
@@ -897,7 +897,7 @@ pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &st
                               -> Option<Spanned<(Symbol, ast::StrStyle)>> {
     // Update `expr.span`'s ctxt now in case expr is an `include!` macro invocation.
     let expr = expr.map(|mut expr| {
-        expr.span = expr.span.with_ctxt(expr.span.ctxt().apply_mark(cx.current_expansion.mark));
+        expr.span = expr.span.apply_mark(cx.current_expansion.mark);
         expr
     });
 
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 269517e998f..062f3ce1127 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -38,11 +38,11 @@ pub trait AstBuilder {
 
     fn qpath(&self, self_type: P<ast::Ty>,
              trait_path: ast::Path,
-             ident: ast::SpannedIdent)
+             ident: ast::Ident)
              -> (ast::QSelf, ast::Path);
     fn qpath_all(&self, self_type: P<ast::Ty>,
                 trait_path: ast::Path,
-                ident: ast::SpannedIdent,
+                ident: ast::Ident,
                 lifetimes: Vec<ast::Lifetime>,
                 types: Vec<P<ast::Ty>>,
                 bindings: Vec<ast::TypeBinding>)
@@ -319,16 +319,18 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
                 types: Vec<P<ast::Ty>>,
                 bindings: Vec<ast::TypeBinding> )
                 -> ast::Path {
-        let last_identifier = idents.pop().unwrap();
+        let last_ident = idents.pop().unwrap();
         let mut segments: Vec<ast::PathSegment> = Vec::new();
 
-        segments.extend(idents.into_iter().map(|i| ast::PathSegment::from_ident(i, span)));
+        segments.extend(idents.into_iter().map(|ident| {
+            ast::PathSegment::from_ident(ident.with_span_pos(span))
+        }));
         let parameters = if !lifetimes.is_empty() || !types.is_empty() || !bindings.is_empty() {
             ast::AngleBracketedParameterData { lifetimes, types, bindings, span }.into()
         } else {
             None
         };
-        segments.push(ast::PathSegment { identifier: last_identifier, span, parameters });
+        segments.push(ast::PathSegment { ident: last_ident.with_span_pos(span), parameters });
         let mut path = ast::Path { span, segments };
         if global {
             if let Some(seg) = path.make_root() {
@@ -344,7 +346,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     fn qpath(&self,
              self_type: P<ast::Ty>,
              trait_path: ast::Path,
-             ident: ast::SpannedIdent)
+             ident: ast::Ident)
              -> (ast::QSelf, ast::Path) {
         self.qpath_all(self_type, trait_path, ident, vec![], vec![], vec![])
     }
@@ -355,7 +357,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     fn qpath_all(&self,
                  self_type: P<ast::Ty>,
                  trait_path: ast::Path,
-                 ident: ast::SpannedIdent,
+                 ident: ast::Ident,
                  lifetimes: Vec<ast::Lifetime>,
                  types: Vec<P<ast::Ty>>,
                  bindings: Vec<ast::TypeBinding>)
@@ -366,11 +368,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         } else {
             None
         };
-        path.segments.push(ast::PathSegment {
-            identifier: ident.node,
-            span: ident.span,
-            parameters,
-        });
+        path.segments.push(ast::PathSegment { ident, parameters });
 
         (ast::QSelf {
             ty: self_type,
@@ -439,17 +437,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
 
     fn typaram(&self,
                span: Span,
-               id: ast::Ident,
+               ident: ast::Ident,
                attrs: Vec<ast::Attribute>,
                bounds: ast::TyParamBounds,
                default: Option<P<ast::Ty>>) -> ast::TyParam {
         ast::TyParam {
-            ident: id,
+            ident: ident.with_span_pos(span),
             id: ast::DUMMY_NODE_ID,
             attrs: attrs.into(),
             bounds,
             default,
-            span,
         }
     }
 
@@ -473,7 +470,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     }
 
     fn lifetime(&self, span: Span, ident: ast::Ident) -> ast::Lifetime {
-        ast::Lifetime { id: ast::DUMMY_NODE_ID, span: span, ident: ident }
+        ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) }
     }
 
     fn lifetime_def(&self,
@@ -636,8 +633,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     }
 
     fn expr_field_access(&self, sp: Span, expr: P<ast::Expr>, ident: ast::Ident) -> P<ast::Expr> {
-        let id = Spanned { node: ident, span: sp };
-        self.expr(sp, ast::ExprKind::Field(expr, id))
+        self.expr(sp, ast::ExprKind::Field(expr, ident.with_span_pos(sp)))
     }
     fn expr_tup_field_access(&self, sp: Span, expr: P<ast::Expr>, idx: usize) -> P<ast::Expr> {
         let id = Spanned { node: idx, span: sp };
@@ -667,14 +663,15 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
                         ident: ast::Ident,
                         mut args: Vec<P<ast::Expr>> ) -> P<ast::Expr> {
         args.insert(0, expr);
-        self.expr(span, ast::ExprKind::MethodCall(ast::PathSegment::from_ident(ident, span), args))
+        let segment = ast::PathSegment::from_ident(ident.with_span_pos(span));
+        self.expr(span, ast::ExprKind::MethodCall(segment, args))
     }
     fn expr_block(&self, b: P<ast::Block>) -> P<ast::Expr> {
         self.expr(b.span, ast::ExprKind::Block(b))
     }
-    fn field_imm(&self, span: Span, name: Ident, e: P<ast::Expr>) -> ast::Field {
+    fn field_imm(&self, span: Span, ident: Ident, e: P<ast::Expr>) -> ast::Field {
         ast::Field {
-            ident: respan(span, name),
+            ident: ident.with_span_pos(span),
             expr: e,
             span,
             is_shorthand: false,
@@ -835,7 +832,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
                               span: Span,
                               ident: ast::Ident,
                               bm: ast::BindingMode) -> P<ast::Pat> {
-        let pat = PatKind::Ident(bm, Spanned{span: span, node: ident}, None);
+        let pat = PatKind::Ident(bm, ident.with_span_pos(span), None);
         self.pat(span, pat)
     }
     fn pat_path(&self, span: Span, path: ast::Path) -> P<ast::Pat> {
@@ -1027,7 +1024,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             body)
     }
 
-    fn variant(&self, span: Span, name: Ident, tys: Vec<P<ast::Ty>> ) -> ast::Variant {
+    fn variant(&self, span: Span, ident: Ident, tys: Vec<P<ast::Ty>> ) -> ast::Variant {
         let fields: Vec<_> = tys.into_iter().map(|ty| {
             ast::StructField {
                 span: ty.span,
@@ -1047,7 +1044,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
 
         respan(span,
                ast::Variant_ {
-                   name,
+                   ident,
                    attrs: Vec::new(),
                    data: vdata,
                    disr_expr: None,
@@ -1132,21 +1129,22 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     }
 
     fn meta_word(&self, sp: Span, w: ast::Name) -> ast::MetaItem {
-        attr::mk_spanned_word_item(sp, w)
+        attr::mk_word_item(Ident::with_empty_ctxt(w).with_span_pos(sp))
     }
 
     fn meta_list_item_word(&self, sp: Span, w: ast::Name) -> ast::NestedMetaItem {
-        respan(sp, ast::NestedMetaItemKind::MetaItem(attr::mk_spanned_word_item(sp, w)))
+        attr::mk_nested_word_item(Ident::with_empty_ctxt(w).with_span_pos(sp))
     }
 
     fn meta_list(&self, sp: Span, name: ast::Name, mis: Vec<ast::NestedMetaItem>)
                  -> ast::MetaItem {
-        attr::mk_spanned_list_item(sp, name, mis)
+        attr::mk_list_item(sp, Ident::with_empty_ctxt(name).with_span_pos(sp), mis)
     }
 
     fn meta_name_value(&self, sp: Span, name: ast::Name, value: ast::LitKind)
                        -> ast::MetaItem {
-        attr::mk_spanned_name_value_item(sp, name, respan(sp, value))
+        attr::mk_name_value_item(sp, Ident::with_empty_ctxt(name).with_span_pos(sp),
+                                 respan(sp, value))
     }
 
     fn item_use(&self, sp: Span,
diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs
index c7fa0331c1b..6bf166dfe95 100644
--- a/src/libsyntax/ext/derive.rs
+++ b/src/libsyntax/ext/derive.rs
@@ -54,7 +54,7 @@ pub fn add_derived_markers<T>(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path]
             pretty_name.push_str(", ");
         }
         pretty_name.push_str(&path.to_string());
-        names.insert(unwrap_or!(path.segments.get(0), continue).identifier.name);
+        names.insert(unwrap_or!(path.segments.get(0), continue).ident.name);
     }
     pretty_name.push(')');
 
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 105de13b976..678c20402d6 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -168,10 +168,10 @@ fn macro_bang_format(path: &ast::Path) -> ExpnFormat {
             path_str.push_str("::");
         }
 
-        if segment.identifier.name != keywords::CrateRoot.name() &&
-            segment.identifier.name != keywords::DollarCrate.name()
+        if segment.ident.name != keywords::CrateRoot.name() &&
+            segment.ident.name != keywords::DollarCrate.name()
         {
-            path_str.push_str(&segment.identifier.name.as_str())
+            path_str.push_str(&segment.ident.name.as_str())
         }
     }
 
@@ -688,7 +688,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             opt_expanded
         } else {
             let msg = format!("non-{kind} macro in {kind} position: {name}",
-                              name = path.segments[0].identifier.name, kind = kind.name());
+                              name = path.segments[0].ident.name, kind = kind.name());
             self.cx.span_err(path.span, &msg);
             self.cx.trace_macros_diag();
             kind.dummy(span)
@@ -733,7 +733,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 invoc.expansion_data.mark.set_expn_info(expn_info);
                 let span = span.with_ctxt(self.cx.backtrace());
                 let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this
-                    name: keywords::Invalid.name(),
+                    ident: keywords::Invalid.ident(),
                     span: DUMMY_SP,
                     node: ast::MetaItemKind::Word,
                 };
@@ -1279,15 +1279,16 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
 
                             let include_info = vec![
                                 dummy_spanned(ast::NestedMetaItemKind::MetaItem(
-                                        attr::mk_name_value_item_str("file".into(),
-                                                                     file))),
+                                        attr::mk_name_value_item_str(Ident::from_str("file"),
+                                                                     dummy_spanned(file)))),
                                 dummy_spanned(ast::NestedMetaItemKind::MetaItem(
-                                        attr::mk_name_value_item_str("contents".into(),
-                                                                     (&*src).into()))),
+                                        attr::mk_name_value_item_str(Ident::from_str("contents"),
+                                                            dummy_spanned(Symbol::intern(&src))))),
                             ];
 
-                            items.push(dummy_spanned(ast::NestedMetaItemKind::MetaItem(
-                                        attr::mk_list_item("include".into(), include_info))));
+                            let include_ident = Ident::from_str("include");
+                            let item = attr::mk_list_item(DUMMY_SP, include_ident, include_info);
+                            items.push(dummy_spanned(ast::NestedMetaItemKind::MetaItem(item)));
                         }
                         Err(_) => {
                             self.cx.span_err(at.span,
@@ -1300,7 +1301,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
                 }
             }
 
-            let meta = attr::mk_list_item("doc".into(), items);
+            let meta = attr::mk_list_item(DUMMY_SP, Ident::from_str("doc"), items);
             match at.style {
                 ast::AttrStyle::Inner =>
                     Some(attr::mk_spanned_attr_inner(at.span, at.id, meta)),
@@ -1378,12 +1379,12 @@ pub struct Marker(pub Mark);
 
 impl Folder for Marker {
     fn fold_ident(&mut self, mut ident: Ident) -> Ident {
-        ident.ctxt = ident.ctxt.apply_mark(self.0);
+        ident.span = ident.span.apply_mark(self.0);
         ident
     }
 
     fn new_span(&mut self, span: Span) -> Span {
-        span.with_ctxt(span.ctxt().apply_mark(self.0))
+        span.apply_mark(self.0)
     }
 
     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs
index 540a03ff032..3303955d398 100644
--- a/src/libsyntax/ext/quote.rs
+++ b/src/libsyntax/ext/quote.rs
@@ -75,7 +75,7 @@ pub mod rt {
 
     impl ToTokens for ast::Ident {
         fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
-            vec![TokenTree::Token(DUMMY_SP, Token::from_ast_ident(*self))]
+            vec![TokenTree::Token(self.span, Token::from_ast_ident(*self))]
         }
     }
 
@@ -193,7 +193,7 @@ pub mod rt {
 
     impl ToTokens for ast::Lifetime {
         fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
-            vec![TokenTree::Token(DUMMY_SP, token::Lifetime(self.ident))]
+            vec![TokenTree::Token(self.ident.span, token::Lifetime(self.ident))]
         }
     }
 
@@ -239,7 +239,7 @@ pub mod rt {
                     inner.push(TokenTree::Token(self.span, token::Colon).into());
                 }
                 inner.push(TokenTree::Token(
-                    self.span, token::Token::from_ast_ident(segment.identifier)
+                    self.span, token::Token::from_ast_ident(segment.ident)
                 ).into());
             }
             inner.push(self.tokens.clone());
diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs
index 8cb331c65da..d9c3deb30da 100644
--- a/src/libsyntax/ext/tt/macro_parser.rs
+++ b/src/libsyntax/ext/tt/macro_parser.rs
@@ -86,7 +86,6 @@ use self::TokenTreeOrTokenTreeVec::*;
 
 use ast::Ident;
 use syntax_pos::{self, BytePos, Span};
-use codemap::respan;
 use errors::FatalError;
 use ext::tt::quoted::{self, TokenTree};
 use parse::{Directory, ParseSess};
@@ -366,7 +365,7 @@ pub fn parse_failure_msg(tok: Token) -> String {
 fn token_name_eq(t1: &Token, t2: &Token) -> bool {
     if let (Some((id1, is_raw1)), Some((id2, is_raw2))) = (t1.ident(), t2.ident()) {
         id1.name == id2.name && is_raw1 == is_raw2
-    } else if let (&token::Lifetime(id1), &token::Lifetime(id2)) = (t1, t2) {
+    } else if let (Some(id1), Some(id2)) = (t1.lifetime(), t2.lifetime()) {
         id1.name == id2.name
     } else {
         *t1 == *t2
@@ -825,8 +824,9 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
         "ty" => token::NtTy(panictry!(p.parse_ty())),
         // this could be handled like a token, since it is one
         "ident" => if let Some((ident, is_raw)) = get_macro_ident(&p.token) {
+            let span = p.span;
             p.bump();
-            token::NtIdent(respan(p.prev_span, ident), is_raw)
+            token::NtIdent(Ident::new(ident.name, span), is_raw)
         } else {
             let token_str = pprust::token_to_string(&p.token);
             p.fatal(&format!("expected ident, found {}", &token_str)).emit();
@@ -835,7 +835,7 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
         "path" => token::NtPath(panictry!(p.parse_path_common(PathStyle::Type, false))),
         "meta" => token::NtMeta(panictry!(p.parse_meta_item())),
         "vis" => token::NtVis(panictry!(p.parse_visibility(true))),
-        "lifetime" => token::NtLifetime(p.expect_lifetime()),
+        "lifetime" => token::NtLifetime(p.expect_lifetime().ident),
         // this is not supposed to happen, since it has been checked
         // when compiling the macro.
         _ => p.span_bug(sp, "invalid fragment specifier"),
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 10e5926eb9e..ffe68289d52 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -53,7 +53,7 @@ impl<'a> ParserAnyMacro<'a> {
         }
 
         // Make sure we don't have any tokens left to parse so we don't silently drop anything.
-        let path = ast::Path::from_ident(site_span, macro_ident);
+        let path = ast::Path::from_ident(macro_ident.with_span_pos(site_span));
         parser.ensure_complete_parse(&path, kind.name(), site_span);
         expansion
     }
diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs
index f324edeb117..01b971976a7 100644
--- a/src/libsyntax/ext/tt/quoted.rs
+++ b/src/libsyntax/ext/tt/quoted.rs
@@ -289,14 +289,11 @@ where
             // `tree` is followed by an `ident`. This could be `$meta_var` or the `$crate` special
             // metavariable that names the crate of the invokation.
             Some(tokenstream::TokenTree::Token(ident_span, ref token)) if token.is_ident() => {
-                let (ident, _) = token.ident().unwrap();
+                let (ident, is_raw) = token.ident().unwrap();
                 let span = ident_span.with_lo(span.lo());
-                if ident.name == keywords::Crate.name() {
-                    let ident = ast::Ident {
-                        name: keywords::DollarCrate.name(),
-                        ..ident
-                    };
-                    TokenTree::Token(span, token::Ident(ident, false))
+                if ident.name == keywords::Crate.name() && !is_raw {
+                    let ident = ast::Ident::new(keywords::DollarCrate.name(), ident.span);
+                    TokenTree::Token(span, token::Ident(ident, is_raw))
                 } else {
                     TokenTree::MetaVar(span, ident)
                 }
diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs
index 3f01d5ec6dd..1cdb6b0e5c9 100644
--- a/src/libsyntax/ext/tt/transcribe.rs
+++ b/src/libsyntax/ext/tt/transcribe.rs
@@ -156,7 +156,7 @@ pub fn transcribe(cx: &ExtCtxt,
                         if let NtTT(ref tt) = **nt {
                             result.push(tt.clone().into());
                         } else {
-                            sp = sp.with_ctxt(sp.ctxt().apply_mark(cx.current_expansion.mark));
+                            sp = sp.apply_mark(cx.current_expansion.mark);
                             let token = TokenTree::Token(sp, Token::interpolated((**nt).clone()));
                             result.push(token.into());
                         }
@@ -166,14 +166,14 @@ pub fn transcribe(cx: &ExtCtxt,
                     }
                 } else {
                     let ident =
-                        Ident { ctxt: ident.ctxt.apply_mark(cx.current_expansion.mark), ..ident };
-                    sp = sp.with_ctxt(sp.ctxt().apply_mark(cx.current_expansion.mark));
+                        Ident::new(ident.name, ident.span.apply_mark(cx.current_expansion.mark));
+                    sp = sp.apply_mark(cx.current_expansion.mark);
                     result.push(TokenTree::Token(sp, token::Dollar).into());
                     result.push(TokenTree::Token(sp, token::Token::from_ast_ident(ident)).into());
                 }
             }
             quoted::TokenTree::Delimited(mut span, delimited) => {
-                span = span.with_ctxt(span.ctxt().apply_mark(cx.current_expansion.mark));
+                span = span.apply_mark(cx.current_expansion.mark);
                 stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span });
                 result_stack.push(mem::replace(&mut result, Vec::new()));
             }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 9d1f4ccf8a3..e1f1d95b8ab 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -1054,7 +1054,7 @@ pub struct GatedCfg {
 
 impl GatedCfg {
     pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
-        let name = cfg.name().as_str();
+        let name = cfg.ident.name.as_str();
         GATED_CFGS.iter()
                   .position(|info| info.0 == name)
                   .map(|idx| {
@@ -1766,11 +1766,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
 
     fn visit_path(&mut self, path: &'a ast::Path, _id: NodeId) {
         for segment in &path.segments {
-            if segment.identifier.name == keywords::Crate.name() {
-                gate_feature_post!(&self, crate_in_paths, segment.span,
+            // Identifiers we are going to check could come from a legacy macro (e.g. `#[test]`).
+            // For such macros identifiers must have empty context, because this context is
+            // used during name resolution and produced names must be unhygienic for compatibility.
+            // On the other hand, we need the actual non-empty context for feature gate checking
+            // because it's hygienic even for legacy macros. As previously stated, such context
+            // cannot be kept in identifiers, so it's kept in paths instead and we take it from
+            // there while keeping location info from the ident span.
+            let span = segment.ident.span.with_ctxt(path.span.ctxt());
+            if segment.ident.name == keywords::Crate.name() {
+                gate_feature_post!(&self, crate_in_paths, span,
                                    "`crate` in paths is experimental");
-            } else if segment.identifier.name == keywords::Extern.name() {
-                gate_feature_post!(&self, extern_in_paths, segment.span,
+            } else if segment.ident.name == keywords::Extern.name() {
+                gate_feature_post!(&self, extern_in_paths, span,
                                    "`extern` in paths is experimental");
             }
         }
@@ -1811,7 +1819,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
                 for mi in list {
 
                     let name = if let Some(word) = mi.word() {
-                        word.name()
+                        word.ident.name
                     } else {
                         span_err!(span_handler, mi.span, E0556,
                                   "malformed feature, expected just one word");
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 1bee67bf360..ba6703b9c74 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -430,7 +430,7 @@ pub fn noop_fold_global_asm<T: Folder>(ga: P<GlobalAsm>,
 pub fn noop_fold_variant<T: Folder>(v: Variant, fld: &mut T) -> Variant {
     Spanned {
         node: Variant_ {
-            name: fld.fold_ident(v.node.name),
+            ident: fld.fold_ident(v.node.ident),
             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)),
@@ -439,8 +439,8 @@ pub fn noop_fold_variant<T: Folder>(v: Variant, fld: &mut T) -> Variant {
     }
 }
 
-pub fn noop_fold_ident<T: Folder>(i: Ident, _: &mut T) -> Ident {
-    i
+pub fn noop_fold_ident<T: Folder>(ident: Ident, fld: &mut T) -> Ident {
+    Ident::new(ident.name, fld.new_span(ident.span))
 }
 
 pub fn noop_fold_usize<T: Folder>(i: usize, _: &mut T) -> usize {
@@ -449,9 +449,8 @@ pub fn noop_fold_usize<T: Folder>(i: usize, _: &mut T) -> usize {
 
 pub fn noop_fold_path<T: Folder>(Path { segments, span }: Path, fld: &mut T) -> Path {
     Path {
-        segments: segments.move_map(|PathSegment {identifier, span, parameters}| PathSegment {
-            identifier: fld.fold_ident(identifier),
-            span: fld.new_span(span),
+        segments: segments.move_map(|PathSegment {ident, parameters}| PathSegment {
+            ident: fld.fold_ident(ident),
             parameters: parameters.map(|ps| ps.map(|ps| fld.fold_path_parameters(ps))),
         }),
         span: fld.new_span(span)
@@ -544,7 +543,7 @@ pub fn noop_fold_meta_list_item<T: Folder>(li: NestedMetaItem, fld: &mut T)
 
 pub fn noop_fold_meta_item<T: Folder>(mi: MetaItem, fld: &mut T) -> MetaItem {
     MetaItem {
-        name: mi.name,
+        ident: mi.ident,
         node: match mi.node {
             MetaItemKind::Word => MetaItemKind::Word,
             MetaItemKind::List(mis) => {
@@ -634,8 +633,8 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T)
         token::NtPat(pat) => token::NtPat(fld.fold_pat(pat)),
         token::NtExpr(expr) => token::NtExpr(fld.fold_expr(expr)),
         token::NtTy(ty) => token::NtTy(fld.fold_ty(ty)),
-        token::NtIdent(id, is_raw) =>
-            token::NtIdent(Spanned::<Ident>{node: fld.fold_ident(id.node), ..id}, is_raw),
+        token::NtIdent(ident, is_raw) => token::NtIdent(fld.fold_ident(ident), is_raw),
+        token::NtLifetime(ident) => token::NtLifetime(fld.fold_ident(ident)),
         token::NtMeta(meta) => token::NtMeta(fld.fold_meta_item(meta)),
         token::NtPath(path) => token::NtPath(fld.fold_path(path)),
         token::NtTT(tt) => token::NtTT(fld.fold_tt(tt)),
@@ -651,7 +650,6 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T)
             token::NtWhereClause(fld.fold_where_clause(where_clause)),
         token::NtArg(arg) => token::NtArg(fld.fold_arg(arg)),
         token::NtVis(vis) => token::NtVis(fld.fold_vis(vis)),
-        token::NtLifetime(lifetime) => token::NtLifetime(fld.fold_lifetime(lifetime)),
         token::NtForeignItem(ni) =>
             token::NtForeignItem(fld.fold_foreign_item(ni)
                                  // see reasoning above
@@ -680,7 +678,7 @@ pub fn noop_fold_ty_param_bound<T>(tpb: TyParamBound, fld: &mut T)
 }
 
 pub fn noop_fold_ty_param<T: Folder>(tp: TyParam, fld: &mut T) -> TyParam {
-    let TyParam {attrs, id, ident, bounds, default, span} = tp;
+    let TyParam {attrs, id, ident, bounds, default} = tp;
     let attrs: Vec<_> = attrs.into();
     TyParam {
         attrs: attrs.into_iter()
@@ -691,7 +689,6 @@ pub fn noop_fold_ty_param<T: Folder>(tp: TyParam, fld: &mut T) -> TyParam {
         ident: fld.fold_ident(ident),
         bounds: fld.fold_bounds(bounds),
         default: default.map(|x| fld.fold_ty(x)),
-        span: fld.new_span(span),
     }
 }
 
@@ -712,7 +709,6 @@ pub fn noop_fold_generic_params<T: Folder>(
 pub fn noop_fold_label<T: Folder>(label: Label, fld: &mut T) -> Label {
     Label {
         ident: fld.fold_ident(label.ident),
-        span: fld.new_span(label.span),
     }
 }
 
@@ -720,7 +716,6 @@ pub fn noop_fold_lifetime<T: Folder>(l: Lifetime, fld: &mut T) -> Lifetime {
     Lifetime {
         id: fld.new_id(l.id),
         ident: fld.fold_ident(l.ident),
-        span: fld.new_span(l.span)
     }
 }
 
@@ -859,7 +854,7 @@ pub fn noop_fold_struct_field<T: Folder>(f: StructField, fld: &mut T) -> StructF
 
 pub fn noop_fold_field<T: Folder>(f: Field, folder: &mut T) -> Field {
     Field {
-        ident: respan(f.ident.span, folder.fold_ident(f.ident.node)),
+        ident: folder.fold_ident(f.ident),
         expr: folder.fold_expr(f.expr),
         span: folder.new_span(f.span),
         is_shorthand: f.is_shorthand,
@@ -1119,11 +1114,10 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
         id: folder.new_id(id),
         node: match node {
             PatKind::Wild => PatKind::Wild,
-            PatKind::Ident(binding_mode, pth1, sub) => {
+            PatKind::Ident(binding_mode, ident, sub) => {
                 PatKind::Ident(binding_mode,
-                        Spanned{span: folder.new_span(pth1.span),
-                                node: folder.fold_ident(pth1.node)},
-                        sub.map(|x| folder.fold_pat(x)))
+                               folder.fold_ident(ident),
+                               sub.map(|x| folder.fold_pat(x)))
             }
             PatKind::Lit(e) => PatKind::Lit(folder.fold_expr(e)),
             PatKind::TupleStruct(pth, pats, ddpos) => {
@@ -1195,8 +1189,7 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
             ExprKind::MethodCall(seg, args) => {
                 ExprKind::MethodCall(
                     PathSegment {
-                        identifier: folder.fold_ident(seg.identifier),
-                        span: folder.new_span(seg.span),
+                        ident: folder.fold_ident(seg.ident),
                         parameters: seg.parameters.map(|ps| {
                             ps.map(|ps| folder.fold_path_parameters(ps))
                         }),
@@ -1272,14 +1265,12 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
                             folder.fold_expr(er))
             }
             ExprKind::Field(el, ident) => {
-                ExprKind::Field(folder.fold_expr(el),
-                          respan(folder.new_span(ident.span),
-                                 folder.fold_ident(ident.node)))
+                ExprKind::Field(folder.fold_expr(el), folder.fold_ident(ident))
             }
-            ExprKind::TupField(el, ident) => {
+            ExprKind::TupField(el, index) => {
                 ExprKind::TupField(folder.fold_expr(el),
-                             respan(folder.new_span(ident.span),
-                                    folder.fold_usize(ident.node)))
+                             respan(folder.new_span(index.span),
+                                    folder.fold_usize(index.node)))
             }
             ExprKind::Index(el, er) => {
                 ExprKind::Index(folder.fold_expr(el), folder.fold_expr(er))
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index 4c3f42d9c6b..90f08ab1468 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -149,8 +149,7 @@ impl<'a> Parser<'a> {
         };
         Ok(if let Some(meta) = meta {
             self.bump();
-            (ast::Path::from_ident(meta.span, ast::Ident::with_empty_ctxt(meta.name)),
-             meta.node.tokens(meta.span))
+            (ast::Path::from_ident(meta.ident), meta.node.tokens(meta.span))
         } else {
             (self.parse_path(PathStyle::Mod)?, self.parse_tokens())
         })
@@ -228,7 +227,7 @@ impl<'a> Parser<'a> {
         let lo = self.span;
         let ident = self.parse_ident()?;
         let node = self.parse_meta_item_kind()?;
-        Ok(ast::MetaItem { name: ident.name, node: node, span: lo.to(self.prev_span) })
+        Ok(ast::MetaItem { ident, node: node, span: lo.to(self.prev_span) })
     }
 
     pub fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index 068929c8948..152c4c31eb3 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -76,7 +76,7 @@ impl<'a> StringReader<'a> {
     fn mk_ident(&self, string: &str) -> Ident {
         let mut ident = Ident::from_str(string);
         if let Some(span) = self.override_span {
-            ident.ctxt = span.ctxt();
+            ident.span = span;
         }
         ident
     }
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 1483691a1ea..428b3b136df 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -689,7 +689,7 @@ mod tests {
     }
 
     fn str2seg(s: &str, lo: u32, hi: u32) -> ast::PathSegment {
-        ast::PathSegment::from_ident(Ident::from_str(s), sp(lo, hi))
+        ast::PathSegment::from_ident(Ident::new(Symbol::intern(s), sp(lo, hi)))
     }
 
     #[test] fn path_exprs_1() {
@@ -873,10 +873,8 @@ mod tests {
                     == P(ast::Pat{
                     id: ast::DUMMY_NODE_ID,
                     node: PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Immutable),
-                                        Spanned{ span:sp(0, 1),
-                                                node: Ident::from_str("b")
-                        },
-                                        None),
+                                         Ident::new(Symbol::intern("b"), sp(0, 1)),
+                                         None),
                     span: sp(0,1)}));
             parser_done(parser);
         })
@@ -912,9 +910,7 @@ mod tests {
                                             node: PatKind::Ident(
                                                 ast::BindingMode::ByValue(
                                                     ast::Mutability::Immutable),
-                                                Spanned{
-                                                    span: sp(6,7),
-                                                    node: Ident::from_str("b")},
+                                                Ident::new(Symbol::intern("b"), sp(6, 7)),
                                                 None
                                             ),
                                             span: sp(6,7)
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 9936eea9356..e6da5bcaa3a 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -512,13 +512,10 @@ impl From<P<Expr>> for LhsExpr {
 
 /// Create a placeholder argument.
 fn dummy_arg(span: Span) -> Arg {
-    let spanned = Spanned {
-        span,
-        node: keywords::Invalid.ident()
-    };
+    let ident = Ident::new(keywords::Invalid.name(), span);
     let pat = P(Pat {
         id: ast::DUMMY_NODE_ID,
-        node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), spanned, None),
+        node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None),
         span,
     });
     let ty = Ty {
@@ -778,7 +775,7 @@ impl<'a> Parser<'a> {
 
     fn parse_ident_common(&mut self, recover: bool) -> PResult<'a, ast::Ident> {
         match self.token {
-            token::Ident(i, _) => {
+            token::Ident(ident, _) => {
                 if self.token.is_reserved_ident() {
                     let mut err = self.expected_ident_found();
                     if recover {
@@ -787,8 +784,9 @@ impl<'a> Parser<'a> {
                         return Err(err);
                     }
                 }
+                let span = self.span;
                 self.bump();
-                Ok(i)
+                Ok(Ident::new(ident.name, span))
             }
             _ => {
                 Err(if self.prev_token_kind == PrevTokenKind::DocComment {
@@ -1319,19 +1317,6 @@ impl<'a> Parser<'a> {
             self.check_keyword(keywords::Extern) && self.is_extern_non_path()
     }
 
-    fn eat_label(&mut self) -> Option<Label> {
-        let ident = match self.token {
-            token::Lifetime(ref ident) => *ident,
-            token::Interpolated(ref nt) => match nt.0 {
-                token::NtLifetime(lifetime) => lifetime.ident,
-                _ => return None,
-            },
-            _ => return None,
-        };
-        self.bump();
-        Some(Label { ident, span: self.prev_span })
-    }
-
     /// parse a TyKind::BareFn type:
     pub fn parse_ty_bare_fn(&mut self, generic_params: Vec<GenericParam>)
                             -> PResult<'a, TyKind> {
@@ -1784,13 +1769,11 @@ impl<'a> Parser<'a> {
             pat
         } else {
             debug!("parse_arg_general ident_to_pat");
-            let sp = self.prev_span;
-            let spanned = Spanned { span: sp, node: keywords::Invalid.ident() };
+            let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
             P(Pat {
                 id: ast::DUMMY_NODE_ID,
-                node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable),
-                                     spanned, None),
-                span: sp
+                node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None),
+                span: ident.span,
             })
         };
 
@@ -1899,13 +1882,14 @@ impl<'a> Parser<'a> {
 
     pub fn parse_path_segment_ident(&mut self) -> PResult<'a, ast::Ident> {
         match self.token {
-            token::Ident(sid, _) if self.token.is_path_segment_keyword() => {
+            token::Ident(ident, _) if self.token.is_path_segment_keyword() => {
+                let span = self.span;
                 self.bump();
-                Ok(sid)
+                Ok(Ident::new(ident.name, span))
             }
             _ => self.parse_ident(),
-         }
-     }
+        }
+    }
 
     /// Parses qualified path.
     /// Assumes that the leading `<` has been parsed already.
@@ -1973,7 +1957,7 @@ impl<'a> Parser<'a> {
         let meta_ident = match self.token {
             token::Interpolated(ref nt) => match nt.0 {
                 token::NtMeta(ref meta) => match meta.node {
-                    ast::MetaItemKind::Word => Some(ast::Ident::with_empty_ctxt(meta.name)),
+                    ast::MetaItemKind::Word => Some(meta.ident),
                     _ => None,
                 },
                 _ => None,
@@ -1982,7 +1966,7 @@ impl<'a> Parser<'a> {
         };
         if let Some(ident) = meta_ident {
             self.bump();
-            return Ok(ast::Path::from_ident(self.prev_span, ident));
+            return Ok(ast::Path::from_ident(ident));
         }
         self.parse_path(style)
     }
@@ -2003,7 +1987,6 @@ impl<'a> Parser<'a> {
 
     fn parse_path_segment(&mut self, style: PathStyle, enable_warning: bool)
                           -> PResult<'a, PathSegment> {
-        let ident_span = self.span;
         let ident = self.parse_path_segment_ident()?;
 
         let is_args_start = |token: &token::Token| match *token {
@@ -2051,10 +2034,10 @@ impl<'a> Parser<'a> {
                 ParenthesizedParameterData { inputs, output, span }.into()
             };
 
-            PathSegment { identifier: ident, span: ident_span, parameters }
+            PathSegment { ident, parameters }
         } else {
             // Generic arguments are not found.
-            PathSegment::from_ident(ident, ident_span)
+            PathSegment::from_ident(ident)
         })
     }
 
@@ -2065,14 +2048,25 @@ impl<'a> Parser<'a> {
 
     /// Parse single lifetime 'a or panic.
     pub fn expect_lifetime(&mut self) -> Lifetime {
-        if let Some(lifetime) = self.token.lifetime(self.span) {
+        if let Some(ident) = self.token.lifetime() {
+            let span = self.span;
             self.bump();
-            lifetime
+            Lifetime { ident: Ident::new(ident.name, span), id: ast::DUMMY_NODE_ID }
         } else {
             self.span_bug(self.span, "not a lifetime")
         }
     }
 
+    fn eat_label(&mut self) -> Option<Label> {
+        if let Some(ident) = self.token.lifetime() {
+            let span = self.span;
+            self.bump();
+            Some(Label { ident: Ident::new(ident.name, span) })
+        } else {
+            None
+        }
+    }
+
     /// Parse mutability (`mut` or nothing).
     fn parse_mutability(&mut self) -> Mutability {
         if self.eat_keyword(keywords::Mut) {
@@ -2085,7 +2079,7 @@ impl<'a> Parser<'a> {
     pub fn parse_field_name(&mut self) -> PResult<'a, Ident> {
         if let token::Literal(token::Integer(name), None) = self.token {
             self.bump();
-            Ok(Ident::with_empty_ctxt(name))
+            Ok(Ident::new(name, self.prev_span))
         } else {
             self.parse_ident_common(false)
         }
@@ -2095,24 +2089,22 @@ impl<'a> Parser<'a> {
     pub fn parse_field(&mut self) -> PResult<'a, Field> {
         let attrs = self.parse_outer_attributes()?;
         let lo = self.span;
-        let hi;
 
         // Check if a colon exists one ahead. This means we're parsing a fieldname.
         let (fieldname, expr, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
             let fieldname = self.parse_field_name()?;
-            hi = self.prev_span;
-            self.bump();
+            self.bump(); // `:`
             (fieldname, self.parse_expr()?, false)
         } else {
             let fieldname = self.parse_ident_common(false)?;
-            hi = self.prev_span;
 
             // Mimic `x: x` for the `x` field shorthand.
-            let path = ast::Path::from_ident(lo.to(hi), fieldname);
-            (fieldname, self.mk_expr(lo.to(hi), ExprKind::Path(None, path), ThinVec::new()), true)
+            let path = ast::Path::from_ident(fieldname);
+            let expr = self.mk_expr(fieldname.span, ExprKind::Path(None, path), ThinVec::new());
+            (fieldname, expr, true)
         };
         Ok(ast::Field {
-            ident: respan(lo.to(hi), fieldname),
+            ident: fieldname,
             span: lo.to(expr.span),
             expr,
             is_shorthand,
@@ -2318,7 +2310,7 @@ impl<'a> Parser<'a> {
                     return self.parse_while_expr(None, lo, attrs);
                 }
                 if let Some(label) = self.eat_label() {
-                    let lo = label.span;
+                    let lo = label.ident.span;
                     self.expect(&token::Colon)?;
                     if self.eat_keyword(keywords::While) {
                         return self.parse_while_expr(Some(label), lo, attrs)
@@ -2592,8 +2584,7 @@ impl<'a> Parser<'a> {
                 }
 
                 let span = lo.to(self.prev_span);
-                let ident = respan(segment.span, segment.identifier);
-                self.mk_expr(span, ExprKind::Field(self_arg, ident), ThinVec::new())
+                self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), ThinVec::new())
             }
         })
     }
@@ -2714,7 +2705,7 @@ impl<'a> Parser<'a> {
     }
 
     pub fn process_potential_macro_variable(&mut self) {
-        let (ident, is_raw) = match self.token {
+        let (token, span) = match self.token {
             token::Dollar if self.span.ctxt() != syntax_pos::hygiene::SyntaxContext::empty() &&
                              self.look_ahead(1, |t| t.is_ident()) => {
                 self.bump();
@@ -2729,15 +2720,18 @@ impl<'a> Parser<'a> {
             }
             token::Interpolated(ref nt) => {
                 self.meta_var_span = Some(self.span);
+                // Interpolated identifier and lifetime tokens are replaced with usual identifier
+                // and lifetime tokens, so the former are never encountered during normal parsing.
                 match nt.0 {
-                    token::NtIdent(ident, is_raw) => (ident, is_raw),
+                    token::NtIdent(ident, is_raw) => (token::Ident(ident, is_raw), ident.span),
+                    token::NtLifetime(ident) => (token::Lifetime(ident), ident.span),
                     _ => return,
                 }
             }
             _ => return,
         };
-        self.token = token::Ident(ident.node, is_raw);
-        self.span = ident.span;
+        self.token = token;
+        self.span = span;
     }
 
     /// parse a single token tree from the input.
@@ -3672,10 +3666,9 @@ impl<'a> Parser<'a> {
                     (false, true) => BindingMode::ByValue(Mutability::Mutable),
                     (false, false) => BindingMode::ByValue(Mutability::Immutable),
                 };
-                let fieldpath = codemap::Spanned{span:self.prev_span, node:fieldname};
                 let fieldpat = P(Pat {
                     id: ast::DUMMY_NODE_ID,
-                    node: PatKind::Ident(bind_type, fieldpath, None),
+                    node: PatKind::Ident(bind_type, fieldname, None),
                     span: boxed_span.to(hi),
                 });
 
@@ -3966,9 +3959,7 @@ impl<'a> Parser<'a> {
     fn parse_pat_ident(&mut self,
                        binding_mode: ast::BindingMode)
                        -> PResult<'a, PatKind> {
-        let ident_span = self.span;
         let ident = self.parse_ident()?;
-        let name = codemap::Spanned{span: ident_span, node: ident};
         let sub = if self.eat(&token::At) {
             Some(self.parse_pat()?)
         } else {
@@ -3987,7 +3978,7 @@ impl<'a> Parser<'a> {
                 "expected identifier, found enum pattern"))
         }
 
-        Ok(PatKind::Ident(binding_mode, name, sub))
+        Ok(PatKind::Ident(binding_mode, ident, sub))
     }
 
     /// Parse a local variable declaration
@@ -4699,7 +4690,6 @@ impl<'a> Parser<'a> {
 
     /// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?
     fn parse_ty_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, TyParam> {
-        let span = self.span;
         let ident = self.parse_ident()?;
 
         // Parse optional colon and param bounds.
@@ -4721,7 +4711,6 @@ impl<'a> Parser<'a> {
             id: ast::DUMMY_NODE_ID,
             bounds,
             default,
-            span,
         })
     }
 
@@ -4729,7 +4718,6 @@ impl<'a> Parser<'a> {
     ///     TraitItemAssocTy = Ident ["<"...">"] [":" [TyParamBounds]] ["where" ...] ["=" Ty]
     fn parse_trait_item_assoc_ty(&mut self, preceding_attrs: Vec<Attribute>)
         -> PResult<'a, (ast::Generics, TyParam)> {
-        let span = self.span;
         let ident = self.parse_ident()?;
         let mut generics = self.parse_generics()?;
 
@@ -4754,7 +4742,6 @@ impl<'a> Parser<'a> {
             id: ast::DUMMY_NODE_ID,
             bounds,
             default,
-            span,
         }))
     }
 
@@ -5051,9 +5038,8 @@ impl<'a> Parser<'a> {
     fn parse_self_arg(&mut self) -> PResult<'a, Option<Arg>> {
         let expect_ident = |this: &mut Self| match this.token {
             // Preserve hygienic context.
-            token::Ident(ident, _) => {
-                let sp = this.span; this.bump(); codemap::respan(sp, ident)
-            }
+            token::Ident(ident, _) =>
+                { let span = this.span; this.bump(); Ident::new(ident.name, span) }
             _ => unreachable!()
         };
         let isolated_self = |this: &mut Self, n| {
@@ -5566,7 +5552,7 @@ impl<'a> Parser<'a> {
                     TyKind::Path(None, path) => path,
                     _ => {
                         self.span_err(ty_first.span, "expected a trait, found type");
-                        ast::Path::from_ident(ty_first.span, keywords::Invalid.ident())
+                        ast::Path::from_ident(Ident::new(keywords::Invalid.name(), ty_first.span))
                     }
                 };
                 let trait_ref = TraitRef { path, ref_id: ty_first.id };
@@ -5951,8 +5937,7 @@ impl<'a> Parser<'a> {
                     let attr = Attribute {
                         id: attr::mk_attr_id(),
                         style: ast::AttrStyle::Outer,
-                        path: ast::Path::from_ident(syntax_pos::DUMMY_SP,
-                                                    Ident::from_str("warn_directory_ownership")),
+                        path: ast::Path::from_ident(Ident::from_str("warn_directory_ownership")),
                         tokens: TokenStream::empty(),
                         is_sugared_doc: false,
                         span: syntax_pos::DUMMY_SP,
@@ -6334,7 +6319,7 @@ impl<'a> Parser<'a> {
             }
 
             let vr = ast::Variant_ {
-                name: ident,
+                ident,
                 attrs: variant_attrs,
                 data: struct_def,
                 disr_expr,
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 5fe2b081566..6544619af9c 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -317,21 +317,36 @@ impl Token {
         }
     }
 
-    pub fn ident(&self) -> Option<(ast::Ident, bool)> {
+    /// Returns an identifier if this token is an identifier.
+    pub fn ident(&self) -> Option<(ast::Ident, /* is_raw */ bool)> {
         match *self {
             Ident(ident, is_raw) => Some((ident, is_raw)),
             Interpolated(ref nt) => match nt.0 {
-                NtIdent(ident, is_raw) => Some((ident.node, is_raw)),
+                NtIdent(ident, is_raw) => Some((ident, is_raw)),
+                _ => None,
+            },
+            _ => None,
+        }
+    }
+    /// Returns a lifetime identifier if this token is a lifetime.
+    pub fn lifetime(&self) -> Option<ast::Ident> {
+        match *self {
+            Lifetime(ident) => Some(ident),
+            Interpolated(ref nt) => match nt.0 {
+                NtLifetime(ident) => Some(ident),
                 _ => None,
             },
             _ => None,
         }
     }
-
     /// Returns `true` if the token is an identifier.
     pub fn is_ident(&self) -> bool {
         self.ident().is_some()
     }
+    /// Returns `true` if the token is a lifetime.
+    pub fn is_lifetime(&self) -> bool {
+        self.lifetime().is_some()
+    }
 
     /// Returns `true` if the token is a documentation comment.
     pub fn is_doc_comment(&self) -> bool {
@@ -359,26 +374,6 @@ impl Token {
         false
     }
 
-    /// Returns a lifetime with the span and a dummy id if it is a lifetime,
-    /// or the original lifetime if it is an interpolated lifetime, ignoring
-    /// the span.
-    pub fn lifetime(&self, span: Span) -> Option<ast::Lifetime> {
-        match *self {
-            Lifetime(ident) =>
-                Some(ast::Lifetime { ident: ident, span: span, id: ast::DUMMY_NODE_ID }),
-            Interpolated(ref nt) => match nt.0 {
-                NtLifetime(lifetime) => Some(lifetime),
-                _ => None,
-            },
-            _ => None,
-        }
-    }
-
-    /// Returns `true` if the token is a lifetime.
-    pub fn is_lifetime(&self) -> bool {
-        self.lifetime(syntax_pos::DUMMY_SP).is_some()
-    }
-
     /// Returns `true` if the token is either the `mut` or `const` keyword.
     pub fn is_mutability(&self) -> bool {
         self.is_keyword(keywords::Mut) ||
@@ -431,6 +426,14 @@ impl Token {
         }
     }
 
+    /// Returns `true` if the token is either a special identifier or a keyword.
+    pub fn is_reserved_ident(&self) -> bool {
+        match self.ident() {
+            Some((id, false)) => is_reserved_ident(id),
+            _ => false,
+        }
+    }
+
     pub fn glue(self, joint: Token) -> Option<Token> {
         Some(match self {
             Eq => match joint {
@@ -497,14 +500,6 @@ impl Token {
         }
     }
 
-    /// Returns `true` if the token is either a special identifier or a keyword.
-    pub fn is_reserved_ident(&self) -> bool {
-        match self.ident() {
-            Some((id, false)) => is_reserved_ident(id),
-            _ => false,
-        }
-    }
-
     pub fn interpolated_to_tokenstream(&self, sess: &ParseSess, span: Span)
         -> TokenStream
     {
@@ -539,12 +534,12 @@ impl Token {
                 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
             }
             Nonterminal::NtIdent(ident, is_raw) => {
-                let token = Token::Ident(ident.node, is_raw);
+                let token = Token::Ident(ident, is_raw);
                 tokens = Some(TokenTree::Token(ident.span, token).into());
             }
-            Nonterminal::NtLifetime(lifetime) => {
-                let token = Token::Lifetime(lifetime.ident);
-                tokens = Some(TokenTree::Token(lifetime.span, token).into());
+            Nonterminal::NtLifetime(ident) => {
+                let token = Token::Lifetime(ident);
+                tokens = Some(TokenTree::Token(ident.span, token).into());
             }
             Nonterminal::NtTT(ref tt) => {
                 tokens = Some(tt.clone().into());
@@ -571,7 +566,8 @@ pub enum Nonterminal {
     NtPat(P<ast::Pat>),
     NtExpr(P<ast::Expr>),
     NtTy(P<ast::Ty>),
-    NtIdent(ast::SpannedIdent, /* is_raw */ bool),
+    NtIdent(ast::Ident, /* is_raw */ bool),
+    NtLifetime(ast::Ident),
     /// Stuff inside brackets for attributes
     NtMeta(ast::MetaItem),
     NtPath(ast::Path),
@@ -585,7 +581,6 @@ pub enum Nonterminal {
     NtGenerics(ast::Generics),
     NtWhereClause(ast::WhereClause),
     NtArg(ast::Arg),
-    NtLifetime(ast::Lifetime),
 }
 
 impl fmt::Debug for Nonterminal {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 1bed6109dd2..8d42206c5cc 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -26,7 +26,7 @@ use print::pp::{self, Breaks};
 use print::pp::Breaks::{Consistent, Inconsistent};
 use ptr::P;
 use std_inject;
-use symbol::{Symbol, keywords};
+use symbol::keywords;
 use syntax_pos::{DUMMY_SP, FileName};
 use tokenstream::{self, TokenStream, TokenTree};
 
@@ -101,13 +101,13 @@ pub fn print_crate<'a>(cm: &'a CodeMap,
         // of the feature gate, so we fake them up here.
 
         // #![feature(prelude_import)]
-        let prelude_import_meta = attr::mk_list_word_item(Symbol::intern("prelude_import"));
-        let list = attr::mk_list_item(Symbol::intern("feature"), vec![prelude_import_meta]);
+        let pi_nested = attr::mk_nested_word_item(ast::Ident::from_str("prelude_import"));
+        let list = attr::mk_list_item(DUMMY_SP, ast::Ident::from_str("feature"), vec![pi_nested]);
         let fake_attr = attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), list);
         s.print_attribute(&fake_attr)?;
 
         // #![no_std]
-        let no_std_meta = attr::mk_word_item(Symbol::intern("no_std"));
+        let no_std_meta = attr::mk_word_item(ast::Ident::from_str("no_std"));
         let fake_attr = attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), no_std_meta);
         s.print_attribute(&fake_attr)?;
     }
@@ -262,26 +262,26 @@ pub fn token_to_string(tok: &Token) -> String {
         token::Shebang(s)           => format!("/* shebang: {}*/", s),
 
         token::Interpolated(ref nt) => match nt.0 {
-            token::NtExpr(ref e)         => expr_to_string(e),
-            token::NtMeta(ref e)         => meta_item_to_string(e),
-            token::NtTy(ref e)           => ty_to_string(e),
-            token::NtPath(ref e)         => path_to_string(e),
-            token::NtItem(ref e)         => item_to_string(e),
-            token::NtBlock(ref e)        => block_to_string(e),
-            token::NtStmt(ref e)         => stmt_to_string(e),
-            token::NtPat(ref e)          => pat_to_string(e),
-            token::NtIdent(ref e, false) => ident_to_string(e.node),
-            token::NtIdent(ref e, true)  => format!("r#{}", ident_to_string(e.node)),
-            token::NtTT(ref tree)        => tt_to_string(tree.clone()),
-            token::NtArm(ref e)          => arm_to_string(e),
-            token::NtImplItem(ref e)     => impl_item_to_string(e),
-            token::NtTraitItem(ref e)    => trait_item_to_string(e),
-            token::NtGenerics(ref e)     => generic_params_to_string(&e.params),
-            token::NtWhereClause(ref e)  => where_clause_to_string(e),
-            token::NtArg(ref e)          => arg_to_string(e),
-            token::NtVis(ref e)          => vis_to_string(e),
-            token::NtLifetime(ref e)     => lifetime_to_string(e),
-            token::NtForeignItem(ref ni) => foreign_item_to_string(ni),
+            token::NtExpr(ref e)        => expr_to_string(e),
+            token::NtMeta(ref e)        => meta_item_to_string(e),
+            token::NtTy(ref e)          => ty_to_string(e),
+            token::NtPath(ref e)        => path_to_string(e),
+            token::NtItem(ref e)        => item_to_string(e),
+            token::NtBlock(ref e)       => block_to_string(e),
+            token::NtStmt(ref e)        => stmt_to_string(e),
+            token::NtPat(ref e)         => pat_to_string(e),
+            token::NtIdent(e, false)    => ident_to_string(e),
+            token::NtIdent(e, true)     => format!("r#{}", ident_to_string(e)),
+            token::NtLifetime(e)        => ident_to_string(e),
+            token::NtTT(ref tree)       => tt_to_string(tree.clone()),
+            token::NtArm(ref e)         => arm_to_string(e),
+            token::NtImplItem(ref e)    => impl_item_to_string(e),
+            token::NtTraitItem(ref e)   => trait_item_to_string(e),
+            token::NtGenerics(ref e)    => generic_params_to_string(&e.params),
+            token::NtWhereClause(ref e) => where_clause_to_string(e),
+            token::NtArg(ref e)         => arg_to_string(e),
+            token::NtVis(ref e)         => vis_to_string(e),
+            token::NtForeignItem(ref e) => foreign_item_to_string(e),
         }
     }
 }
@@ -739,11 +739,11 @@ pub trait PrintState<'a> {
                     if i > 0 {
                         self.writer().word("::")?
                     }
-                    if segment.identifier.name != keywords::CrateRoot.name() &&
-                       segment.identifier.name != keywords::DollarCrate.name() {
-                        self.writer().word(&segment.identifier.name.as_str())?;
-                    } else if segment.identifier.name == keywords::DollarCrate.name() {
-                        self.print_dollar_crate(segment.identifier.ctxt)?;
+                    if segment.ident.name != keywords::CrateRoot.name() &&
+                       segment.ident.name != keywords::DollarCrate.name() {
+                        self.writer().word(&segment.ident.name.as_str())?;
+                    } else if segment.ident.name == keywords::DollarCrate.name() {
+                        self.print_dollar_crate(segment.ident.span.ctxt())?;
                     }
                 }
                 self.writer().space()?;
@@ -768,15 +768,15 @@ pub trait PrintState<'a> {
         self.ibox(INDENT_UNIT)?;
         match item.node {
             ast::MetaItemKind::Word => {
-                self.writer().word(&item.name.as_str())?;
+                self.writer().word(&item.ident.name.as_str())?;
             }
             ast::MetaItemKind::NameValue(ref value) => {
-                self.word_space(&item.name.as_str())?;
+                self.word_space(&item.ident.name.as_str())?;
                 self.word_space("=")?;
                 self.print_literal(value)?;
             }
             ast::MetaItemKind::List(ref items) => {
-                self.writer().word(&item.name.as_str())?;
+                self.writer().word(&item.ident.name.as_str())?;
                 self.popen()?;
                 self.commasep(Consistent,
                               &items[..],
@@ -1540,7 +1540,7 @@ impl<'a> State<'a> {
     pub fn print_variant(&mut self, v: &ast::Variant) -> io::Result<()> {
         self.head("")?;
         let generics = ast::Generics::default();
-        self.print_struct(&v.node.data, &generics, v.node.name, v.span, false)?;
+        self.print_struct(&v.node.data, &generics, v.node.ident, v.span, false)?;
         match v.node.disr_expr {
             Some(ref d) => {
                 self.s.space()?;
@@ -1924,7 +1924,7 @@ impl<'a> State<'a> {
             |s, field| {
                 s.ibox(INDENT_UNIT)?;
                 if !field.is_shorthand {
-                    s.print_ident(field.ident.node)?;
+                    s.print_ident(field.ident)?;
                     s.word_space(":")?;
                 }
                 s.print_expr(&field.expr)?;
@@ -1981,7 +1981,7 @@ impl<'a> State<'a> {
         let base_args = &args[1..];
         self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX)?;
         self.s.word(".")?;
-        self.print_ident(segment.identifier)?;
+        self.print_ident(segment.ident)?;
         if let Some(ref parameters) = segment.parameters {
             self.print_path_parameters(parameters, true)?;
         }
@@ -2198,10 +2198,10 @@ impl<'a> State<'a> {
                 self.word_space("=")?;
                 self.print_expr_maybe_paren(rhs, prec)?;
             }
-            ast::ExprKind::Field(ref expr, id) => {
+            ast::ExprKind::Field(ref expr, ident) => {
                 self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX)?;
                 self.s.word(".")?;
-                self.print_ident(id.node)?;
+                self.print_ident(ident)?;
             }
             ast::ExprKind::TupField(ref expr, id) => {
                 self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX)?;
@@ -2417,14 +2417,14 @@ impl<'a> State<'a> {
                           colons_before_params: bool)
                           -> io::Result<()>
     {
-        if segment.identifier.name != keywords::CrateRoot.name() &&
-           segment.identifier.name != keywords::DollarCrate.name() {
-            self.print_ident(segment.identifier)?;
+        if segment.ident.name != keywords::CrateRoot.name() &&
+           segment.ident.name != keywords::DollarCrate.name() {
+            self.print_ident(segment.ident)?;
             if let Some(ref parameters) = segment.parameters {
                 self.print_path_parameters(parameters, colons_before_params)?;
             }
-        } else if segment.identifier.name == keywords::DollarCrate.name() {
-            self.print_dollar_crate(segment.identifier.ctxt)?;
+        } else if segment.ident.name == keywords::DollarCrate.name() {
+            self.print_dollar_crate(segment.ident.span.ctxt())?;
         }
         Ok(())
     }
@@ -2446,7 +2446,7 @@ impl<'a> State<'a> {
         self.s.word(">")?;
         self.s.word("::")?;
         let item_segment = path.segments.last().unwrap();
-        self.print_ident(item_segment.identifier)?;
+        self.print_ident(item_segment.ident)?;
         match item_segment.parameters {
             Some(ref parameters) => self.print_path_parameters(parameters, colons_before_params),
             None => Ok(()),
@@ -2526,7 +2526,7 @@ impl<'a> State<'a> {
          is that it doesn't matter */
         match pat.node {
             PatKind::Wild => self.s.word("_")?,
-            PatKind::Ident(binding_mode, ref path1, ref sub) => {
+            PatKind::Ident(binding_mode, ident, ref sub) => {
                 match binding_mode {
                     ast::BindingMode::ByRef(mutbl) => {
                         self.word_nbsp("ref")?;
@@ -2537,7 +2537,7 @@ impl<'a> State<'a> {
                         self.word_nbsp("mut")?;
                     }
                 }
-                self.print_ident(path1.node)?;
+                self.print_ident(ident)?;
                 if let Some(ref p) = *sub {
                     self.s.word("@")?;
                     self.print_pat(p)?;
@@ -2999,7 +2999,7 @@ impl<'a> State<'a> {
                     self.print_explicit_self(&eself)?;
                 } else {
                     let invalid = if let PatKind::Ident(_, ident, _) = input.pat.node {
-                        ident.node.name == keywords::Invalid.name()
+                        ident.name == keywords::Invalid.name()
                     } else {
                         false
                     };
@@ -3194,7 +3194,7 @@ mod tests {
             let ident = ast::Ident::from_str("principal_skinner");
 
             let var = codemap::respan(syntax_pos::DUMMY_SP, ast::Variant_ {
-                name: ident,
+                ident,
                 attrs: Vec::new(),
                 // making this up as I go.... ?
                 data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs
index fdbc795b2d3..63d7b3336a8 100644
--- a/src/libsyntax/std_inject.rs
+++ b/src/libsyntax/std_inject.rs
@@ -57,7 +57,7 @@ pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str>
     krate.module.items.insert(0, P(ast::Item {
         attrs: vec![attr::mk_attr_outer(DUMMY_SP,
                                         attr::mk_attr_id(),
-                                        attr::mk_word_item(Symbol::intern("macro_use")))],
+                                        attr::mk_word_item(ast::Ident::from_str("macro_use")))],
         vis: dummy_spanned(ast::VisibilityKind::Inherited),
         node: ast::ItemKind::ExternCrate(alt_std_name.map(Symbol::intern)),
         ident: ast::Ident::from_str(name),
@@ -70,7 +70,7 @@ pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str>
     krate.module.items.insert(0, P(ast::Item {
         attrs: vec![ast::Attribute {
             style: ast::AttrStyle::Outer,
-            path: ast::Path::from_ident(span, ast::Ident::from_str("prelude_import")),
+            path: ast::Path::from_ident(ast::Ident::new(Symbol::intern("prelude_import"), span)),
             tokens: TokenStream::empty(),
             id: attr::mk_attr_id(),
             is_sugared_doc: false,
@@ -80,7 +80,7 @@ pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str>
         node: ast::ItemKind::Use(P(ast::UseTree {
             prefix: ast::Path {
                 segments: [name, "prelude", "v1"].into_iter().map(|name| {
-                    ast::PathSegment::from_ident(ast::Ident::from_str(name), DUMMY_SP)
+                    ast::PathSegment::from_ident(ast::Ident::from_str(name))
                 }).collect(),
                 span,
             },
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 67a822e4e02..fd2e760e9be 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -195,10 +195,10 @@ impl fold::Folder for EntryPointCleaner {
             EntryPointType::MainAttr |
             EntryPointType::Start =>
                 folded.map(|ast::Item {id, ident, attrs, node, vis, span, tokens}| {
-                    let allow_str = Symbol::intern("allow");
-                    let dead_code_str = Symbol::intern("dead_code");
-                    let word_vec = vec![attr::mk_list_word_item(dead_code_str)];
-                    let allow_dead_code_item = attr::mk_list_item(allow_str, word_vec);
+                    let allow_ident = Ident::from_str("allow");
+                    let dc_nested = attr::mk_nested_word_item(Ident::from_str("dead_code"));
+                    let allow_dead_code_item = attr::mk_list_item(DUMMY_SP, allow_ident,
+                                                                  vec![dc_nested]);
                     let allow_dead_code = attr::mk_attr_outer(DUMMY_SP,
                                                               attr::mk_attr_id(),
                                                               allow_dead_code_item);
@@ -623,7 +623,7 @@ fn nospan<T>(t: T) -> codemap::Spanned<T> {
 fn path_node(ids: Vec<Ident>) -> ast::Path {
     ast::Path {
         span: DUMMY_SP,
-        segments: ids.into_iter().map(|id| ast::PathSegment::from_ident(id, DUMMY_SP)).collect(),
+        segments: ids.into_iter().map(|id| ast::PathSegment::from_ident(id)).collect(),
     }
 }
 
diff --git a/src/libsyntax/util/node_count.rs b/src/libsyntax/util/node_count.rs
index da238be85e0..d4c6b4b158b 100644
--- a/src/libsyntax/util/node_count.rs
+++ b/src/libsyntax/util/node_count.rs
@@ -27,9 +27,9 @@ impl NodeCounter {
 }
 
 impl<'ast> Visitor<'ast> for NodeCounter {
-    fn visit_ident(&mut self, span: Span, ident: Ident) {
+    fn visit_ident(&mut self, ident: Ident) {
         self.count += 1;
-        walk_ident(self, span, ident);
+        walk_ident(self, ident);
     }
     fn visit_mod(&mut self, m: &Mod, _s: Span, _a: &[Attribute], _n: NodeId) {
         self.count += 1;
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 3a2069efd89..fdb3e2c5f31 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -55,8 +55,8 @@ pub trait Visitor<'ast>: Sized {
     fn visit_name(&mut self, _span: Span, _name: Name) {
         // Nothing to do.
     }
-    fn visit_ident(&mut self, span: Span, ident: Ident) {
-        walk_ident(self, span, ident);
+    fn visit_ident(&mut self, ident: Ident) {
+        walk_ident(self, ident);
     }
     fn visit_mod(&mut self, m: &'ast Mod, _s: Span, _attrs: &[Attribute], _n: NodeId) {
         walk_mod(self, m);
@@ -166,8 +166,8 @@ macro_rules! walk_list {
     }
 }
 
-pub fn walk_ident<'a, V: Visitor<'a>>(visitor: &mut V, span: Span, ident: Ident) {
-    visitor.visit_name(span, ident.name);
+pub fn walk_ident<'a, V: Visitor<'a>>(visitor: &mut V, ident: Ident) {
+    visitor.visit_name(ident.span, ident.name);
 }
 
 pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) {
@@ -189,11 +189,11 @@ pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) {
 }
 
 pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, label: &'a Label) {
-    visitor.visit_ident(label.span, label.ident);
+    visitor.visit_ident(label.ident);
 }
 
 pub fn walk_lifetime<'a, V: Visitor<'a>>(visitor: &mut V, lifetime: &'a Lifetime) {
-    visitor.visit_ident(lifetime.span, lifetime.ident);
+    visitor.visit_ident(lifetime.ident);
 }
 
 pub fn walk_poly_trait_ref<'a, V>(visitor: &mut V,
@@ -211,7 +211,7 @@ pub fn walk_trait_ref<'a, V: Visitor<'a>>(visitor: &mut V, trait_ref: &'a TraitR
 
 pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
     visitor.visit_vis(&item.vis);
-    visitor.visit_ident(item.span, item.ident);
+    visitor.visit_ident(item.ident);
     match item.node {
         ItemKind::ExternCrate(orig_name) => {
             if let Some(orig_name) = orig_name {
@@ -293,8 +293,8 @@ pub fn walk_variant<'a, V>(visitor: &mut V,
                            item_id: NodeId)
     where V: Visitor<'a>,
 {
-    visitor.visit_ident(variant.span, variant.node.name);
-    visitor.visit_variant_data(&variant.node.data, variant.node.name,
+    visitor.visit_ident(variant.node.ident);
+    visitor.visit_variant_data(&variant.node.data, variant.node.ident,
                              generics, item_id, variant.span);
     walk_list!(visitor, visit_expr, &variant.node.disr_expr);
     walk_list!(visitor, visit_attribute, &variant.node.attrs);
@@ -357,7 +357,7 @@ pub fn walk_use_tree<'a, V: Visitor<'a>>(
     match use_tree.kind {
         UseTreeKind::Simple(rename) => {
             if let Some(rename) = rename {
-                visitor.visit_ident(use_tree.span, rename);
+                visitor.visit_ident(rename);
             }
         }
         UseTreeKind::Glob => {},
@@ -372,7 +372,7 @@ pub fn walk_use_tree<'a, V: Visitor<'a>>(
 pub fn walk_path_segment<'a, V: Visitor<'a>>(visitor: &mut V,
                                              path_span: Span,
                                              segment: &'a PathSegment) {
-    visitor.visit_ident(path_span, segment.identifier);
+    visitor.visit_ident(segment.ident);
     if let Some(ref parameters) = segment.parameters {
         visitor.visit_path_parameters(path_span, parameters);
     }
@@ -398,7 +398,7 @@ pub fn walk_path_parameters<'a, V>(visitor: &mut V,
 
 pub fn walk_assoc_type_binding<'a, V: Visitor<'a>>(visitor: &mut V,
                                                    type_binding: &'a TypeBinding) {
-    visitor.visit_ident(type_binding.span, type_binding.ident);
+    visitor.visit_ident(type_binding.ident);
     visitor.visit_ty(&type_binding.ty);
 }
 
@@ -418,7 +418,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
             visitor.visit_path(path, pattern.id);
             for field in fields {
                 walk_list!(visitor, visit_attribute, field.node.attrs.iter());
-                visitor.visit_ident(field.span, field.node.ident);
+                visitor.visit_ident(field.node.ident);
                 visitor.visit_pat(&field.node.pat)
             }
         }
@@ -430,8 +430,8 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
         PatKind::Paren(ref subpattern) => {
             visitor.visit_pat(subpattern)
         }
-        PatKind::Ident(_, ref pth1, ref optional_subpattern) => {
-            visitor.visit_ident(pth1.span, pth1.node);
+        PatKind::Ident(_, ident, ref optional_subpattern) => {
+            visitor.visit_ident(ident);
             walk_list!(visitor, visit_pat, optional_subpattern);
         }
         PatKind::Lit(ref expression) => visitor.visit_expr(expression),
@@ -451,7 +451,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
 
 pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, foreign_item: &'a ForeignItem) {
     visitor.visit_vis(&foreign_item.vis);
-    visitor.visit_ident(foreign_item.span, foreign_item.ident);
+    visitor.visit_ident(foreign_item.ident);
 
     match foreign_item.node {
         ForeignItemKind::Fn(ref function_declaration, ref generics) => {
@@ -489,7 +489,7 @@ pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Generi
             walk_list!(visitor, visit_attribute, &*l.attrs);
         }
         GenericParam::Type(ref t) => {
-            visitor.visit_ident(t.span, t.ident);
+            visitor.visit_ident(t.ident);
             walk_list!(visitor, visit_ty_param_bound, &t.bounds);
             walk_list!(visitor, visit_ty, &t.default);
             walk_list!(visitor, visit_attribute, &*t.attrs);
@@ -561,7 +561,7 @@ pub fn walk_fn<'a, V>(visitor: &mut V, kind: FnKind<'a>, declaration: &'a FnDecl
 }
 
 pub fn walk_trait_item<'a, V: Visitor<'a>>(visitor: &mut V, trait_item: &'a TraitItem) {
-    visitor.visit_ident(trait_item.span, trait_item.ident);
+    visitor.visit_ident(trait_item.ident);
     walk_list!(visitor, visit_attribute, &trait_item.attrs);
     visitor.visit_generics(&trait_item.generics);
     match trait_item.node {
@@ -588,7 +588,7 @@ pub fn walk_trait_item<'a, V: Visitor<'a>>(visitor: &mut V, trait_item: &'a Trai
 
 pub fn walk_impl_item<'a, V: Visitor<'a>>(visitor: &mut V, impl_item: &'a ImplItem) {
     visitor.visit_vis(&impl_item.vis);
-    visitor.visit_ident(impl_item.span, impl_item.ident);
+    visitor.visit_ident(impl_item.ident);
     walk_list!(visitor, visit_attribute, &impl_item.attrs);
     visitor.visit_generics(&impl_item.generics);
     match impl_item.node {
@@ -616,7 +616,7 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(visitor: &mut V, struct_definition: &
 pub fn walk_struct_field<'a, V: Visitor<'a>>(visitor: &mut V, struct_field: &'a StructField) {
     visitor.visit_vis(&struct_field.vis);
     if let Some(ident) = struct_field.ident {
-        visitor.visit_ident(struct_field.span, ident);
+        visitor.visit_ident(ident);
     }
     visitor.visit_ty(&struct_field.ty);
     walk_list!(visitor, visit_attribute, &struct_field.attrs);
@@ -666,7 +666,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             visitor.visit_path(path, expression.id);
             for field in fields {
                 walk_list!(visitor, visit_attribute, field.attrs.iter());
-                visitor.visit_ident(field.ident.span, field.ident.node);
+                visitor.visit_ident(field.ident);
                 visitor.visit_expr(&field.expr)
             }
             walk_list!(visitor, visit_expr, optional_base);
@@ -745,9 +745,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             visitor.visit_expr(left_expression);
             visitor.visit_expr(right_expression);
         }
-        ExprKind::Field(ref subexpression, ref ident) => {
+        ExprKind::Field(ref subexpression, ident) => {
             visitor.visit_expr(subexpression);
-            visitor.visit_ident(ident.span, ident.node);
+            visitor.visit_ident(ident);
         }
         ExprKind::TupField(ref subexpression, _) => {
             visitor.visit_expr(subexpression);
diff --git a/src/libsyntax_ext/assert.rs b/src/libsyntax_ext/assert.rs
index 8b29e6adeb9..7352c494a42 100644
--- a/src/libsyntax_ext/assert.rs
+++ b/src/libsyntax_ext/assert.rs
@@ -14,6 +14,7 @@ use syntax::ext::base::*;
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
 use syntax::print::pprust;
+use syntax::symbol::Symbol;
 use syntax::tokenstream::{TokenStream, TokenTree};
 use syntax_pos::{Span, DUMMY_SP};
 
@@ -35,9 +36,9 @@ pub fn expand_assert<'cx>(
         None
     };
 
-    let sp = sp.with_ctxt(sp.ctxt().apply_mark(cx.current_expansion.mark));
+    let sp = sp.apply_mark(cx.current_expansion.mark);
     let panic_call = Mac_ {
-        path: Path::from_ident(sp, Ident::from_str("panic")),
+        path: Path::from_ident(Ident::new(Symbol::intern("panic"), sp)),
         tts: if let Some(ts) = custom_msg_args {
             ts.into()
         } else {
diff --git a/src/libsyntax_ext/cfg.rs b/src/libsyntax_ext/cfg.rs
index 1eeba9b30b8..6acc578d07e 100644
--- a/src/libsyntax_ext/cfg.rs
+++ b/src/libsyntax_ext/cfg.rs
@@ -24,7 +24,7 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
                        sp: Span,
                        tts: &[tokenstream::TokenTree])
                        -> Box<base::MacResult + 'static> {
-    let sp = sp.with_ctxt(sp.ctxt().apply_mark(cx.current_expansion.mark));
+    let sp = sp.apply_mark(cx.current_expansion.mark);
     let mut p = cx.new_parser_from_tts(tts);
     let cfg = panictry!(p.parse_meta_item());
 
diff --git a/src/libsyntax_ext/concat.rs b/src/libsyntax_ext/concat.rs
index c79e7867c5f..6c085528a66 100644
--- a/src/libsyntax_ext/concat.rs
+++ b/src/libsyntax_ext/concat.rs
@@ -57,6 +57,6 @@ pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
             }
         }
     }
-    let sp = sp.with_ctxt(sp.ctxt().apply_mark(cx.current_expansion.mark));
+    let sp = sp.apply_mark(cx.current_expansion.mark);
     base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&accumulator)))
 }
diff --git a/src/libsyntax_ext/concat_idents.rs b/src/libsyntax_ext/concat_idents.rs
index d513008f0e2..544b1410d3d 100644
--- a/src/libsyntax_ext/concat_idents.rs
+++ b/src/libsyntax_ext/concat_idents.rs
@@ -16,7 +16,6 @@ use syntax::parse::token;
 use syntax::ptr::P;
 use syntax_pos::Span;
 use syntax_pos::symbol::Symbol;
-use syntax_pos::hygiene::SyntaxContext;
 use syntax::tokenstream::TokenTree;
 
 pub fn expand_syntax_ext<'cx>(cx: &'cx mut ExtCtxt,
@@ -53,31 +52,17 @@ pub fn expand_syntax_ext<'cx>(cx: &'cx mut ExtCtxt,
             }
         }
     }
-    let res = ast::Ident {
-        name: Symbol::intern(&res_str),
-        ctxt: SyntaxContext::empty().apply_mark(cx.current_expansion.mark),
-    };
 
-    struct Result {
-        ident: ast::Ident,
-        span: Span,
-    };
+    let ident = ast::Ident::new(Symbol::intern(&res_str), sp.apply_mark(cx.current_expansion.mark));
 
-    impl Result {
-        fn path(&self) -> ast::Path {
-            ast::Path {
-                span: self.span,
-                segments: vec![ast::PathSegment::from_ident(self.ident, self.span)],
-            }
-        }
-    }
+    struct ConcatIdentsResult { ident: ast::Ident }
 
-    impl base::MacResult for Result {
+    impl base::MacResult for ConcatIdentsResult {
         fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
             Some(P(ast::Expr {
                 id: ast::DUMMY_NODE_ID,
-                node: ast::ExprKind::Path(None, self.path()),
-                span: self.span,
+                node: ast::ExprKind::Path(None, ast::Path::from_ident(self.ident)),
+                span: self.ident.span,
                 attrs: ast::ThinVec::new(),
             }))
         }
@@ -85,14 +70,11 @@ pub fn expand_syntax_ext<'cx>(cx: &'cx mut ExtCtxt,
         fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
             Some(P(ast::Ty {
                 id: ast::DUMMY_NODE_ID,
-                node: ast::TyKind::Path(None, self.path()),
-                span: self.span,
+                node: ast::TyKind::Path(None, ast::Path::from_ident(self.ident)),
+                span: self.ident.span,
             }))
         }
     }
 
-    Box::new(Result {
-        ident: res,
-        span: sp.with_ctxt(sp.ctxt().apply_mark(cx.current_expansion.mark)),
-    })
+    Box::new(ConcatIdentsResult { ident })
 }
diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs
index f23d22b0c36..dec24d13c9b 100644
--- a/src/libsyntax_ext/deriving/clone.rs
+++ b/src/libsyntax_ext/deriving/clone.rs
@@ -177,7 +177,7 @@ fn cs_clone(name: &str,
             vdata = vdata_;
         }
         EnumMatching(.., variant, ref af) => {
-            ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]);
+            ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.ident]);
             all_fields = af;
             vdata = &variant.node.data;
         }
diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs
index 7b23de582a7..c007285cd85 100644
--- a/src/libsyntax_ext/deriving/debug.rs
+++ b/src/libsyntax_ext/deriving/debug.rs
@@ -61,7 +61,7 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<E
     // based on the "shape".
     let (ident, is_struct) = match *substr.fields {
         Struct(vdata, _) => (substr.type_ident, vdata.is_struct()),
-        EnumMatching(_, _, v, _) => (v.node.name, v.node.data.is_struct()),
+        EnumMatching(_, _, v, _) => (v.node.ident, v.node.data.is_struct()),
         EnumNonMatchingCollapsed(..) |
         StaticStruct(..) |
         StaticEnum(..) => cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`"),
diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs
index 88baa22e7fa..d579b3abd45 100644
--- a/src/libsyntax_ext/deriving/encodable.rs
+++ b/src/libsyntax_ext/deriving/encodable.rs
@@ -277,7 +277,7 @@ fn encodable_substructure(cx: &mut ExtCtxt,
             }
 
             let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
-            let name = cx.expr_str(trait_span, variant.node.name.name);
+            let name = cx.expr_str(trait_span, variant.node.ident.name);
             let call = cx.expr_method_call(trait_span,
                                            blkencoder,
                                            cx.ident_of("emit_enum_variant"),
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index 3935f1722b6..4126ce79f35 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -195,6 +195,7 @@ use syntax::abi::Abi;
 use syntax::ast::{
     self, BinOpKind, EnumDef, Expr, GenericParam, Generics, Ident, PatKind, VariantData
 };
+
 use syntax::attr;
 use syntax::ext::base::{Annotatable, ExtCtxt};
 use syntax::ext::build::AstBuilder;
@@ -367,7 +368,7 @@ fn find_type_parameters(ty: &ast::Ty,
         fn visit_ty(&mut self, ty: &'a ast::Ty) {
             if let ast::TyKind::Path(_, ref path) = ty.node {
                 if let Some(segment) = path.segments.first() {
-                    if self.ty_param_names.contains(&segment.identifier.name) {
+                    if self.ty_param_names.contains(&segment.ident.name) {
                         self.types.push(P(ty.clone()));
                     }
                 }
@@ -622,7 +623,7 @@ impl<'a> TraitDef<'a> {
                         // if we have already handled this type, skip it
                         if let ast::TyKind::Path(_, ref p) = ty.node {
                             if p.segments.len() == 1 &&
-                            ty_param_names.contains(&p.segments[0].identifier.name) ||
+                            ty_param_names.contains(&p.segments[0].ident.name) ||
                             processed_field_types.contains(&p.segments) {
                                 continue;
                             };
@@ -952,7 +953,7 @@ impl<'a> MethodDef<'a> {
         let args = {
             let self_args = explicit_self.map(|explicit_self| {
                 ast::Arg::from_self(explicit_self,
-                                    respan(trait_.span, keywords::SelfValue.ident()))
+                                    keywords::SelfValue.ident().with_span_pos(trait_.span))
             });
             let nonself_args = arg_types.into_iter()
                 .map(|(name, ty)| cx.arg(trait_.span, name, ty));
@@ -1537,10 +1538,9 @@ impl<'a> MethodDef<'a> {
         let summary = enum_def.variants
             .iter()
             .map(|v| {
-                let ident = v.node.name;
                 let sp = v.span.with_ctxt(trait_.span.ctxt());
                 let summary = trait_.summarise_struct(cx, &v.node.data);
-                (ident, sp, summary)
+                (v.node.ident, sp, summary)
             })
             .collect();
         self.call_substructure_method(cx,
@@ -1581,7 +1581,7 @@ impl<'a> TraitDef<'a> {
 
     fn create_subpatterns(&self,
                           cx: &mut ExtCtxt,
-                          field_paths: Vec<ast::SpannedIdent>,
+                          field_paths: Vec<ast::Ident>,
                           mutbl: ast::Mutability,
                           use_temporaries: bool)
                           -> Vec<P<ast::Pat>> {
@@ -1613,10 +1613,7 @@ impl<'a> TraitDef<'a> {
         for (i, struct_field) in struct_def.fields().iter().enumerate() {
             let sp = struct_field.span.with_ctxt(self.span.ctxt());
             let ident = cx.ident_of(&format!("{}_{}", prefix, i));
-            paths.push(codemap::Spanned {
-                span: sp,
-                node: ident,
-            });
+            paths.push(ident.with_span_pos(sp));
             let val = cx.expr_path(cx.path_ident(sp, ident));
             let val = if use_temporaries {
                 val
@@ -1669,9 +1666,8 @@ impl<'a> TraitDef<'a> {
          prefix: &str,
          mutbl: ast::Mutability)
          -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) {
-        let variant_ident = variant.node.name;
         let sp = variant.span.with_ctxt(self.span.ctxt());
-        let variant_path = cx.path(sp, vec![enum_ident, variant_ident]);
+        let variant_path = cx.path(sp, vec![enum_ident, variant.node.ident]);
         let use_temporaries = false; // enums can't be repr(packed)
         self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl,
                                    use_temporaries)
diff --git a/src/libsyntax_ext/deriving/generic/ty.rs b/src/libsyntax_ext/deriving/generic/ty.rs
index cc3e8b6e6b7..25a29694488 100644
--- a/src/libsyntax_ext/deriving/generic/ty.rs
+++ b/src/libsyntax_ext/deriving/generic/ty.rs
@@ -18,10 +18,9 @@ use syntax::ast;
 use syntax::ast::{Expr, GenericParam, Generics, Ident, SelfKind};
 use syntax::ext::base::ExtCtxt;
 use syntax::ext::build::AstBuilder;
-use syntax::codemap::respan;
+use syntax::codemap::{respan, DUMMY_SP};
 use syntax::ptr::P;
 use syntax_pos::Span;
-use syntax_pos::hygiene::SyntaxContext;
 use syntax_pos::symbol::keywords;
 
 /// The types of pointers
@@ -93,8 +92,8 @@ impl<'a> Path<'a> {
             PathKind::Global => cx.path_all(span, true, idents, lt, tys, Vec::new()),
             PathKind::Local => cx.path_all(span, false, idents, lt, tys, Vec::new()),
             PathKind::Std => {
-                let def_site = SyntaxContext::empty().apply_mark(cx.current_expansion.mark);
-                idents.insert(0, Ident { ctxt: def_site, ..keywords::DollarCrate.ident() });
+                let def_site = DUMMY_SP.apply_mark(cx.current_expansion.mark);
+                idents.insert(0, Ident::new(keywords::DollarCrate.name(), def_site));
                 cx.path_all(span, false, idents, lt, tys, Vec::new())
             }
         }
diff --git a/src/libsyntax_ext/env.rs b/src/libsyntax_ext/env.rs
index ba6d25f7a60..4e1af108ab4 100644
--- a/src/libsyntax_ext/env.rs
+++ b/src/libsyntax_ext/env.rs
@@ -32,7 +32,7 @@ pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt,
         Some(v) => v,
     };
 
-    let sp = sp.with_ctxt(sp.ctxt().apply_mark(cx.current_expansion.mark));
+    let sp = sp.apply_mark(cx.current_expansion.mark);
     let e = match env::var(&*var.as_str()) {
         Err(..) => {
             let lt = cx.lifetime(sp, keywords::StaticLifetime.ident());
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index d9c68e3167b..c78decb1eb9 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -641,7 +641,7 @@ impl<'a, 'b> Context<'a, 'b> {
                   ty: &ArgumentType,
                   arg: ast::Ident)
                   -> P<ast::Expr> {
-        sp = sp.with_ctxt(sp.ctxt().apply_mark(ecx.current_expansion.mark));
+        sp = sp.apply_mark(ecx.current_expansion.mark);
         let arg = ecx.expr_ident(sp, arg);
         let trait_ = match *ty {
             Placeholder(ref tyname) => {
@@ -678,7 +678,7 @@ pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt,
                                mut sp: Span,
                                tts: &[tokenstream::TokenTree])
                                -> Box<base::MacResult + 'cx> {
-    sp = sp.with_ctxt(sp.ctxt().apply_mark(ecx.current_expansion.mark));
+    sp = sp.apply_mark(ecx.current_expansion.mark);
     match parse_args(ecx, sp, tts) {
         Some((efmt, args, names)) => {
             MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt, args, names))
@@ -700,7 +700,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
     let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
     let arg_unique_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
     let mut macsp = ecx.call_site();
-    macsp = macsp.with_ctxt(macsp.ctxt().apply_mark(ecx.current_expansion.mark));
+    macsp = macsp.apply_mark(ecx.current_expansion.mark);
     let msg = "format argument must be a string literal.";
     let fmt = match expr_to_spanned_string(ecx, efmt, msg) {
         Some(fmt) => fmt,
diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs
index e623779ce63..d684e8b4ffe 100644
--- a/src/libsyntax_ext/proc_macro_registrar.rs
+++ b/src/libsyntax_ext/proc_macro_registrar.rs
@@ -18,7 +18,7 @@ use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute, respan};
 use syntax::ext::base::ExtCtxt;
 use syntax::ext::build::AstBuilder;
 use syntax::ext::expand::ExpansionConfig;
-use syntax::ext::hygiene::{Mark, SyntaxContext};
+use syntax::ext::hygiene::Mark;
 use syntax::fold::Folder;
 use syntax::parse::ParseSess;
 use syntax::ptr::P;
@@ -371,7 +371,7 @@ fn mk_registrar(cx: &mut ExtCtxt,
             allow_internal_unsafe: false,
         }
     });
-    let span = DUMMY_SP.with_ctxt(SyntaxContext::empty().apply_mark(mark));
+    let span = DUMMY_SP.apply_mark(mark);
 
     let proc_macro = Ident::from_str("proc_macro");
     let krate = cx.item(span,
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index aba71bd0468..c180563450f 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -123,7 +123,7 @@ pub struct HygieneData {
     marks: Vec<MarkData>,
     syntax_contexts: Vec<SyntaxContextData>,
     markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
-    gensym_to_ctxt: HashMap<Symbol, SyntaxContext>,
+    gensym_to_ctxt: HashMap<Symbol, Span>,
 }
 
 impl HygieneData {
@@ -461,7 +461,7 @@ 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);
+            data.gensym_to_ctxt.insert(gensym, ident.span);
             gensym
         })
     }
@@ -469,7 +469,7 @@ impl Symbol {
     pub fn to_ident(self) -> Ident {
         HygieneData::with(|data| {
             match data.gensym_to_ctxt.get(&self) {
-                Some(&ctxt) => Ident { name: self.interned(), ctxt: ctxt },
+                Some(&span) => Ident::new(self.interned(), span),
                 None => Ident::with_empty_ctxt(self),
             }
         })
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index eb345200f41..8b2ebfef250 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -50,7 +50,7 @@ extern crate serialize as rustc_serialize; // used by deriving
 extern crate unicode_width;
 
 pub mod hygiene;
-pub use hygiene::{SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind};
+pub use hygiene::{Mark, SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind};
 
 mod span_encoding;
 pub use span_encoding::{Span, DUMMY_SP};
@@ -422,6 +422,52 @@ impl Span {
             if end.ctxt == SyntaxContext::empty() { end.ctxt } else { span.ctxt },
         )
     }
+
+    #[inline]
+    pub fn apply_mark(self, mark: Mark) -> Span {
+        let span = self.data();
+        span.with_ctxt(span.ctxt.apply_mark(mark))
+    }
+
+    #[inline]
+    pub fn remove_mark(&mut self) -> Mark {
+        let mut span = self.data();
+        let mark = span.ctxt.remove_mark();
+        *self = Span::new(span.lo, span.hi, span.ctxt);
+        mark
+    }
+
+    #[inline]
+    pub fn adjust(&mut self, expansion: Mark) -> Option<Mark> {
+        let mut span = self.data();
+        let mark = span.ctxt.adjust(expansion);
+        *self = Span::new(span.lo, span.hi, span.ctxt);
+        mark
+    }
+
+    #[inline]
+    pub fn glob_adjust(&mut self, expansion: Mark, glob_ctxt: SyntaxContext)
+                       -> Option<Option<Mark>> {
+        let mut span = self.data();
+        let mark = span.ctxt.glob_adjust(expansion, glob_ctxt);
+        *self = Span::new(span.lo, span.hi, span.ctxt);
+        mark
+    }
+
+    #[inline]
+    pub fn reverse_glob_adjust(&mut self, expansion: Mark, glob_ctxt: SyntaxContext)
+                               -> Option<Option<Mark>> {
+        let mut span = self.data();
+        let mark = span.ctxt.reverse_glob_adjust(expansion, glob_ctxt);
+        *self = Span::new(span.lo, span.hi, span.ctxt);
+        mark
+    }
+
+    #[inline]
+    pub fn modern(self) -> Span {
+        let span = self.data();
+        span.with_ctxt(span.ctxt.modern())
+    }
 }
 
 #[derive(Clone, Debug)]
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 098eafef258..331b0fe5481 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -13,21 +13,27 @@
 //! type, and vice versa.
 
 use hygiene::SyntaxContext;
-use GLOBALS;
+use {Span, DUMMY_SP, GLOBALS};
 
 use serialize::{Decodable, Decoder, Encodable, Encoder};
 use std::collections::HashMap;
 use std::fmt;
+use std::hash::{Hash, Hasher};
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, Eq)]
 pub struct Ident {
     pub name: Symbol,
-    pub ctxt: SyntaxContext,
+    pub span: Span,
 }
 
 impl Ident {
+    #[inline]
+    pub const fn new(name: Symbol, span: Span) -> Ident {
+        Ident { name, span }
+    }
+    #[inline]
     pub const fn with_empty_ctxt(name: Symbol) -> Ident {
-        Ident { name: name, ctxt: SyntaxContext::empty() }
+        Ident::new(name, DUMMY_SP)
     }
 
     /// Maps a string to an identifier with an empty syntax context.
@@ -35,18 +41,36 @@ impl Ident {
         Ident::with_empty_ctxt(Symbol::intern(string))
     }
 
-    pub fn without_first_quote(&self) -> Ident {
-        Ident { name: Symbol::from(self.name.as_str().trim_left_matches('\'')), ctxt: self.ctxt }
+    /// Replace `lo` and `hi` with those from `span`, but keep hygiene context.
+    pub fn with_span_pos(self, span: Span) -> Ident {
+        Ident::new(self.name, span.with_ctxt(self.span.ctxt()))
+    }
+
+    pub fn without_first_quote(self) -> Ident {
+        Ident::new(Symbol::intern(self.name.as_str().trim_left_matches('\'')), self.span)
     }
 
     pub fn modern(self) -> Ident {
-        Ident { name: self.name, ctxt: self.ctxt.modern() }
+        Ident::new(self.name, self.span.modern())
+    }
+}
+
+impl PartialEq for Ident {
+    fn eq(&self, rhs: &Self) -> bool {
+        self.name == rhs.name && self.span.ctxt() == rhs.span.ctxt()
+    }
+}
+
+impl Hash for Ident {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.name.hash(state);
+        self.span.ctxt().hash(state);
     }
 }
 
 impl fmt::Debug for Ident {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}{:?}", self.name, self.ctxt)
+        write!(f, "{}{:?}", self.name, self.span.ctxt())
     }
 }
 
@@ -58,7 +82,7 @@ impl fmt::Display for Ident {
 
 impl Encodable for Ident {
     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        if self.ctxt.modern() == SyntaxContext::empty() {
+        if self.span.ctxt().modern() == SyntaxContext::empty() {
             s.emit_str(&self.name.as_str())
         } else { // FIXME(jseyfried) intercrate hygiene
             let mut string = "#".to_owned();
@@ -123,12 +147,6 @@ impl Symbol {
     }
 }
 
-impl<'a> From<&'a str> for Symbol {
-    fn from(string: &'a str) -> Symbol {
-        Symbol::intern(string)
-    }
-}
-
 impl fmt::Debug for Symbol {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         let is_gensymed = with_interner(|interner| interner.is_gensymed(*self));
diff --git a/src/test/compile-fail-fulldeps/auxiliary/macro_crate_test.rs b/src/test/compile-fail-fulldeps/auxiliary/macro_crate_test.rs
index bc51b4061ed..040f0b661be 100644
--- a/src/test/compile-fail-fulldeps/auxiliary/macro_crate_test.rs
+++ b/src/test/compile-fail-fulldeps/auxiliary/macro_crate_test.rs
@@ -117,7 +117,7 @@ fn expand_duplicate(cx: &mut ExtCtxt,
     let copy_name = match mi.node {
         ast::MetaItemKind::List(ref xs) => {
             if let Some(word) = xs[0].word() {
-                ast::Ident::with_empty_ctxt(word.name())
+                word.ident
             } else {
                 cx.span_err(mi.span, "Expected word");
                 return;
diff --git a/src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs b/src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs
index 6612fe45b81..2f80408ac1c 100644
--- a/src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs
+++ b/src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs
@@ -112,7 +112,7 @@ fn expand_duplicate(cx: &mut ExtCtxt,
     let copy_name = match mi.node {
         ast::MetaItemKind::List(ref xs) => {
             if let Some(word) = xs[0].word() {
-                ast::Ident::with_empty_ctxt(word.name())
+                word.ident
             } else {
                 cx.span_err(mi.span, "Expected word");
                 return;
diff --git a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs
index 920760cd34a..3a848a31c4d 100644
--- a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs
+++ b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs
@@ -61,15 +61,8 @@ fn expr(kind: ExprKind) -> P<Expr> {
 }
 
 fn make_x() -> P<Expr> {
-    let seg = PathSegment {
-        identifier: Ident::from_str("x"),
-        span: DUMMY_SP,
-        parameters: None,
-    };
-    let path = Path {
-        span: DUMMY_SP,
-        segments: vec![seg],
-    };
+    let seg = PathSegment::from_ident(Ident::from_str("x"));
+    let path = Path { segments: vec![seg], span: DUMMY_SP };
     expr(ExprKind::Path(None, path))
 }
 
@@ -89,12 +82,7 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P<Expr>)) {
             0 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Box(e))),
             1 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Call(e, vec![]))),
             2 => {
-                let seg = PathSegment {
-                    identifier: Ident::from_str("x"),
-                    span: DUMMY_SP,
-                    parameters: None,
-                };
-
+                let seg = PathSegment::from_ident(Ident::from_str("x"));
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall(
                             seg.clone(), vec![e, make_x()])));
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall(
@@ -146,8 +134,7 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P<Expr>)) {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e)));
             },
             10 => {
-                let ident = Spanned { span: DUMMY_SP, node: Ident::from_str("f") };
-                iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, ident)));
+                iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, Ident::from_str("f"))));
             },
             11 => {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Range(
@@ -163,15 +150,7 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P<Expr>)) {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Ret(Some(e))));
             },
             14 => {
-                let seg = PathSegment {
-                    identifier: Ident::from_str("S"),
-                    span: DUMMY_SP,
-                    parameters: None,
-                };
-                let path = Path {
-                    span: DUMMY_SP,
-                    segments: vec![seg],
-                };
+                let path = Path::from_ident(Ident::from_str("S"));
                 g(ExprKind::Struct(path, vec![], Some(make_x())));
             },
             15 => {
diff --git a/src/test/ui/feature-gate-non_ascii_idents.stderr b/src/test/ui/feature-gate-non_ascii_idents.stderr
index 69d7e38d431..a2853570d6d 100644
--- a/src/test/ui/feature-gate-non_ascii_idents.stderr
+++ b/src/test/ui/feature-gate-non_ascii_idents.stderr
@@ -1,8 +1,8 @@
 error[E0658]: non-ascii idents are not fully supported. (see issue #28979)
-  --> $DIR/feature-gate-non_ascii_idents.rs:11:1
+  --> $DIR/feature-gate-non_ascii_idents.rs:11:22
    |
 LL | extern crate core as bäz; //~ ERROR non-ascii idents
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                      ^^^
    |
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable
 
@@ -10,29 +10,23 @@ error[E0658]: non-ascii idents are not fully supported. (see issue #28979)
   --> $DIR/feature-gate-non_ascii_idents.rs:13:5
    |
 LL | use föö::bar; //~ ERROR non-ascii idents
-   |     ^^^^^^^^
+   |     ^^^
    |
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable
 
 error[E0658]: non-ascii idents are not fully supported. (see issue #28979)
-  --> $DIR/feature-gate-non_ascii_idents.rs:15:1
+  --> $DIR/feature-gate-non_ascii_idents.rs:15:5
    |
 LL | mod föö { //~ ERROR non-ascii idents
-   | ^^^^^^^
+   |     ^^^
    |
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable
 
 error[E0658]: non-ascii idents are not fully supported. (see issue #28979)
-  --> $DIR/feature-gate-non_ascii_idents.rs:19:1
+  --> $DIR/feature-gate-non_ascii_idents.rs:19:4
    |
-LL | / fn bär( //~ ERROR non-ascii idents
-LL | |     bäz: isize //~ ERROR non-ascii idents
-LL | |     ) {
-LL | |     let _ö: isize; //~ ERROR non-ascii idents
-...  |
-LL | |     }
-LL | | }
-   | |_^
+LL | fn bär( //~ ERROR non-ascii idents
+   |    ^^^
    |
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable
 
@@ -61,10 +55,10 @@ LL |         (_ä, _) => {} //~ ERROR non-ascii idents
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable
 
 error[E0658]: non-ascii idents are not fully supported. (see issue #28979)
-  --> $DIR/feature-gate-non_ascii_idents.rs:29:1
+  --> $DIR/feature-gate-non_ascii_idents.rs:29:8
    |
 LL | struct Föö { //~ ERROR non-ascii idents
-   | ^^^^^^^^^^
+   |        ^^^
    |
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable
 
@@ -72,15 +66,15 @@ error[E0658]: non-ascii idents are not fully supported. (see issue #28979)
   --> $DIR/feature-gate-non_ascii_idents.rs:30:5
    |
 LL |     föö: isize //~ ERROR non-ascii idents
-   |     ^^^^^^^^^^
+   |     ^^^
    |
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable
 
 error[E0658]: non-ascii idents are not fully supported. (see issue #28979)
-  --> $DIR/feature-gate-non_ascii_idents.rs:33:1
+  --> $DIR/feature-gate-non_ascii_idents.rs:33:6
    |
 LL | enum Bär { //~ ERROR non-ascii idents
-   | ^^^^^^^^
+   |      ^^^
    |
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable
 
@@ -96,15 +90,15 @@ error[E0658]: non-ascii idents are not fully supported. (see issue #28979)
   --> $DIR/feature-gate-non_ascii_idents.rs:35:9
    |
 LL |         qüx: isize //~ ERROR non-ascii idents
-   |         ^^^^^^^^^^
+   |         ^^^
    |
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable
 
 error[E0658]: non-ascii idents are not fully supported. (see issue #28979)
-  --> $DIR/feature-gate-non_ascii_idents.rs:40:5
+  --> $DIR/feature-gate-non_ascii_idents.rs:40:8
    |
 LL |     fn qüx();  //~ ERROR non-ascii idents
-   |     ^^^^^^^^^
+   |        ^^^
    |
    = help: add #![feature(non_ascii_idents)] to the crate attributes to enable